当前位置: 首页 > news >正文

pyspark实现基于协同过滤的电影推荐系统

最近在学一门大数据的课,课程要求很开放,任意做一个大数据相关的项目即可,不知道为什么我就想到推荐算法,一直到着手要做之前还没有新的更好的来代替,那就这个吧。

推荐算法

推荐算法的发展由来已久,但和机器学习一样,没有大量的算力和数据作支撑达不到好的效果,直到如今高速网络和大数据时代才发展得如火如荼。

推荐算法具体有几种:协同过滤,基于内容,基于知识,和混合过滤,具体的知识可以看该文章,简单总结如下:

协同过滤:基于分类的推荐,把用户或物品按类划分,在同类中互相推荐对方的喜好,比如两个都喜欢看动作电影的用户,其中一个又喜欢看爱情片,就把爱情片也推给另一个同样喜欢看动作电影的用户。
该方法根据划分的对象不同,又可以分为基于用户和基于物品两种,后面会对协同过滤算法作稍微详细的介绍。

基于内容:该方案逻辑更为严谨,可以通过用户画像,设定的大致喜好来推荐,该算法需要对物品的特征进行计算,找到最符合用户喜好的几个物品进行推荐。

基于知识:这单独分类就有点牵强了,基于知识的推荐其实就是更严谨的协同过滤和内容推荐,该算法会构造用户的需求列表,根据该列表与物品的相似程度进行推荐,比如高档耐用品用户在满足后就没有了需求,像房子,汽车等,当用户拥有以后就没必要再继续推送。

混合过滤:顾名思义,就是结合多种方法的推荐算法,比如把前面三种方法输入到一个神经网络,最后拟合出一个结果来,希望结合各自的优点,克服他们的缺点。

协同过滤

该算法的核心思想是将被推荐者的同类找出来,基于用户和基于物品的区别,举个例子就是分别找到和你一样喜欢看动作电影的人,把他的其他喜欢推荐给你;和找到和动作电影比较类似的其他电影,把这些电影推荐给喜欢看动作电影的人,这两步殊途同归,而同时都有一个重点——如何衡量相似

相似是我们日常形容的一种感觉,而对计算机来说是可以用数学量化计算的,如余弦相似度,欧几里得距离,皮尔森系数等,其他详细计算方法可见该文章。

相似度计算

本项目中以皮尔斯相关系数作为相似度的衡量标准,表示两个矩阵协方差与标准差的商,计算公式如图:
皮尔斯相关系数
具体计算方法可见该文章,我们编程实现时更多关注其运算规则和返回值即可,该方法返回一个矩阵,对角元素表示其与自身的相似度—为1[0,1][1,0]位置为两矩阵的相似度,该计算方法在np库中有现成的使用方法,示例如下:

    # 计算两个用户的皮尔逊相关系数,越靠近1,表示越相关# 返回相关系数矩阵2*2的矩阵,对角线表示与自身的相关系数,[0, 1](或[1, 0],它们是相等的)则是matrix1和matrix2之间的相关系数pearson=np.corrcoef(matrix1, matrix2)[0, 1]

相似度取值为[-1,1]越大表示越相关,计算出相似度后即相当于找到类似的群体,再推荐该群体的喜好给用户即可。

但在大数据时代中,在动辄几十上百万条的数据里逐条记录计算相似度并推送不那么优雅也不那么准确,并且矩阵中大部分数据都为0,是一个稀疏矩阵,因为很少有人能评价数据库中的所有物品,故数学家研究能不能把这么多记录的大数据矩阵拆成更小的矩阵呢?

SVD奇异值矩阵分解

奇异值矩阵分解可以实现将m×n的矩阵拆解为m×m正交阵、m×n对角阵,多的行列全为0、n×n正交阵三个矩阵,我们可能回想,这拆完其实比之前还大,拆解还有什么意义?
其中对角阵对角线的值我们称为奇异值,奇异值从大到小排列,所以通常我们使用前几位的奇异值组成对角阵,就已经可以大差不差地构造出原有矩阵来,具体原理见该文章。即我们可以将该矩阵压缩为m×rr×rr×n三个矩阵,此时的r我们可以自由选取,这样一来就可以大大压缩矩阵的空间了,实现代码展示如下:

arr1=np.random.randint(0,100,(5,5))
print(arr1)
# svd 分解得到矩阵
a,b,c=np.linalg.svd(arr1)
# 为了节省内存,对角阵使用一维数组保存,故需将其展开
b=np.diag(b)
# 一维数组值到对角线,其余填充0

分别输出后展示如下:
svd分解矩阵展示
随后我们尝试对该矩阵进行压缩,计算后得到的矩阵分别取前k位,方法代码如下:

def get_svd(matrix,k):U, sigma, VT = np.linalg.svd(matrix)# 主对角线数组值,其余均为0,展开sigma=np.diag(sigma)# 截取前k个奇异值U_k=U[:, :k]sigma_k=sigma[:k, :k]VT_k=VT[:k, ]return U_k, sigma_k, VT_k

k取3,即5维矩阵保留前3维奇异值,执行展示如下:
svd压缩
可以看到损失变化并不很大,此时因为我们的矩阵并不稀疏,且压缩率为百分之二十,所以可以说压缩效果相当好。

隐向量

原本纵坐标代表用户,横坐标代表物品,压缩后矩阵不完整,取而代之的是r,那这个r代表什么呢?我在视频中看到如下例子(原视频链接)
原始矩阵
隐向量矩阵
这是一个音乐推荐系统,原有矩阵经过分解,压缩的r取3,用户和歌曲被一个3×3的矩阵联系起来,我们感性上可以把他认为是歌曲种类,但这并不准确,因为r的值具体取多少并不固定,这类似于机器学习中的网络,网络把我们的特征计算得到一个结果,这个结果并没有什么实在意义,但计算机能计算并拟合就足够了,我们将其理解称为计算机做得一个分类也就可以了,这个分类在矩阵分解中被称为隐向量

矩阵分解的应用

说到这我们还没明白,拆解了矩阵以后又怎么样呢?我们从大矩阵得到了小矩阵,并且能利用该小矩阵近似恢复出大矩阵,我们在推荐的时候到底该怎么用这个矩阵呢?

在协同过滤中,使用矩阵分解方法时,我们实际上是在根据用户向量来生成推荐物品矩阵的预测,即获得了r×n的矩阵后,由用户1×r的输入特征,即可获得1×n的用户关于所有物品喜好程度的预测矩阵,我们推荐时只需根据该矩阵即可。
我们由以往基于用户或物品计算相似,粗糙地选择相似者喜好推荐的方法,转变为生成一张近似但量化的,关于用户对所有物品喜好的特征向量来进行推荐,相比较矩阵分解方法更为优雅和准确。

但到这里我想了一个问题,这种方式还叫协同过滤吗?,并没有直接和其他用户和物品协同,这种纯数学的方法是否还符合协同过滤的定义呢?有关问题我问了文心一言,他的解答我直接放在下面以供参考
矩阵分解和协同过滤的关系

数据处理

数据集来自该链接,目录如下:
电影数据集目录
不同文件分别为:影评、电影信息、演员信息、评分信息和用户信息,其中电影又包含电影长度、海报等等信息,具体介绍可以见其中的README
该数据集可以说十分详细,但稍微有些旧,是2019年的,另外是实在太大了,我要构建矩阵时pycharm提示需要153GB的内存。
内存报错信息
以目前想当然来看,应该可以是分批多次读取再组合的,但是因为本次只是一个尝试,就不研究真实大数据条件下的操作了,重点侧重与算法,与项目的完成上,故首先要对数据进行缩减。

先从电影入手,把不包含海报链接的去掉,再把没有豆瓣评分和演员信息等的去掉,最终只保留下两千条数据,再使用这两千条电影的id,在评分文件ratings过滤记录,代码如下:

import pandas as pdfile_source='E:\\software\\DataBase\\Data\\movie_data\\new_movies.csv'
old_source='E:\\software\\DataBase\\Data\\movie\\ratings.csv'
save=pd.read_csv(file_source)
data=pd.read_csv(old_source)
# 获取指定列的值
value_tosave=save["MOVIE_ID"]# 过滤后的数据
filtered_data = data[data['MOVIE_ID'].isin(value_tosave)]# 保存过滤后的数据
filtered_data.to_csv('E:\\software\\DataBase\\Data\\movie\\new_ratings.csv', index=False)

大概步骤为:
先获取电影文件中的电影ID
在评分文件中匹配该ID,符合的保存在变量中;
最后将数据写入csv文件。

同理需要使用排名文件对用户文件进行过滤,此时注意,因为用户名包含中文信息,故在保存csv文件时需要指定编码为utf-8,故保存代码修改为filtered_data.to_csv('E:\\software\\DataBase\\Data\\movie\\new_users.csv', index=False,encoding='utf-8-sig')

过滤前后文件大小对比如下:
文件大小对比
电影数目由十四万降至两千八,打分数目由一百四十万降至十九万,用户数量由六十四万降至八万,此时的数据量计算机大概可以处理了。

但裁后计算svd发现,还是需要50个G的内存,看来分布式系统是不可不用了。
裁后矩阵

Spark系统

大数据框架

之前总是专注算法,对大数据还是一直停留在直观印象上,觉得无非就是数据大一些,记录多一些,但突然想到,这些表象最直接地反映在编程思想上:以往的程序单机运行即可,速度慢的时候只要换一台新的服务器就能得到很好的效果,但到了大数据时代,单个服务器再强也是不可能满足运行要求,故在编程中就要采用分治思想,使用分布式进行计算

目前主流的大数据框架有Hadoopspark,详细介绍和对比可见该文章,二者都是Apache的框架,目前将其理解对比如下:
Hadoop:2012年提出的第一款大数据框架,主要理念为map/reduce,其中map负责数据集的拆解,reduce负责对拆解后的数据集进行运算,其中隐含层为shuffle负责对拆解的数据进行排序归并等操作,总的来说是将大数据采用总分总的形式运行,借助分布式文件系统HDFS实现,如果有计算集群则可以大幅度提升运行效率。
Spark:2014年提出的新一代大数据框架,改进了原有map/reduce,转而使用RAM进行数据暂存,相比更快,并用弹性数据集RDD取代原本HDFS,并且支持Hadoop,其详细教程可看该视频,和极客教程。但具体来说其仅为分布式计算框架,解决了Hadoop原有基于磁盘的效率问题,用内存操作代替了频繁读写磁盘,序列化等步骤,数据在执行完毕前不落地,加快了计算效率,而存储结构仍使用原有的分布式文件系统HDFS

安装部署

本次实验在Windows上进行,但因为没有服务器集群,故部署为单机,大致部署思路如下:
1,配置Java环境,spark是基于Java编写的,必须要有相应的Java环境。
2,安装spark包,官网下载解压到目录即可。
3,配置环境变量
4,bin文件替换,spark是基于Linux系统,若要在Windows中使用需要替换相关文件

具体操作基本可以参考该文章,本地部署无需使用hadoop的可以将相关操作全部指向spark文件即可,配环境确实十分困难,最后我也不知道怎么配好的,甚至没法复现也不敢再重新来过,目前的教训就是一定注意版本对应

RDD

spark的数据操作基于弹性数据集RDD完成,具体操作可见该文章,一次简单读取文件并输出的示例如下:

from pyspark import SparkContext,SparkConf
# 根据对象名和部署模式获取sparkcontext对象
conf=SparkConf().setAppName("test").setMaster("local")
sc=pyspark.SparkContext(conf=conf)
path="file:///E:\\software\\DataBase\\Data\\movie_data\\new_movies.csv"
# 对象读取文件,获得RDD
data=sc.textFile(path)
# 或者使用parallelize生成对象集合
data=sc.parallelize(List(1,2,3,4))
# 遍历输出
data.foreach(print)

操作函数分为转换和动作两种,其中转换类有

filter(func) 		# 过滤,满足func的元素返回新的数据集
map(func)			# 映射,元素传入func中,计算返回数据集
flatMap(func)		# 与map相似,但输入元素可映射0到多个结果
groupByKey()		# 将键值对的数据集按键分组
reduceByKey(func)	# 分组后用func进行计算

动作类函数有:

count()			# 返回元素个数
collect() 		# 以数组形式返回元素
reduce(func)	# 通过func聚合元素
foreach(func)	# 每个元素传递到func中运行
first()			# 返回第一个元素
take(n)			# 返回前n个元素

spark在编程使用时的确具有很多不同特性,作为了解记录如下:
1,惰性机制。spark在执行中采取有向无环图的形式,每个转换操作如map只是记录动作,只有遇到动作reduce才开始执行。
2,RDD只读。RDD在执行生成后不可修改,我们操作的新变量实质是其拷贝版本,这些不同的拷贝构成了有向无环图的节点,这给我们恢复系统状态提供了方便,同时因为计算不结束不落地,减少磁盘开销,提高了效率。
3,粗颗粒度。只能整体转换,不能只修改其中一条。

DataFrame

RDD只给了数据按行操作的能力,有时我们要对数据进行按列的操作甚至更细比如按标签操作时就无法实现了,以及非关系型数据难以用于机器学习的困境,基于此SparkSQL提供了类似pandas的DataFrame操作,其大概原理是将sql语句转为spark,该方法给spark处理大规模结构化数据的能力,同时也比RDD更简单易用,且计算效率更高。

大概使用方法如下:
from pyspark.sql import SparkSession导入包
spark=SparkSession.builder.config(conf=SparkConf()).getOrCreate()创建实例对象

df=spark.read.csv(path)从文件读入,或使用spark_df = spark.createDataFrame(pandas_df)pandas数据中读入创建DataFrame
df.show()展示数据
df.select(列名)实现按列选取数据
df.write.csv(new_path)保存数据

部分操作函数如下:
df.printSchema()展示结构信息
df.filter()条件如df["age"]>10实现按条件过滤
df.groupby("列名")实现分组
df.sort(df['age'].desc())实现按年龄降序排序

工程实践

其实该算法最关键的就是根据已有的打分矩阵计算svd分解并压缩后的矩阵VV矩阵与用户输入矩阵做矩阵乘法就可以生成一张用户关于每部电影的预测矩阵,后续可以根据该矩阵给用户推荐,关于spark使用svd的官方教程在此,目前打算构建成一个前后端交互项目,前端将用户打分数据传到后台,后台推荐算法把推荐的电影信息给前端展示,流程大概如下:
推荐系统流程图
其中后端推荐算法主要是将用户特征向量与V矩阵做矩阵乘法获得预测矩阵,根据该矩阵给用户推荐电影,流程图如下:
推荐系统流程图
文件代码结构如下:
代码结构
其中internet文件负责处理网络信息,main为主函数,matrix_calculate负责把大打分矩阵拆解压缩出V矩阵,recommend实现给用户推荐的具体算法。

主函数

因为本项目为前后端交互,故采用socket模式,死循环内监听端口,接受处理信息,并发送信息给客户端,主函数代码如下:

import recommend
import pandas as pd
import socket
import internet
score_matrix=pd.read_csv('"E:\\software\\DataBase\\Data\\movie_data\\ratings_matrix.csv',header=0,index_col=0)
predict_matrix=pd.read_csv('"E:\\software\\DataBase\\Data\\movie_data\\predict_matrix.csv',header=0,index_col=0)
if __name__ == '__main__':sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 实例化对象sock.bind(('ip', 1111))  # 绑定IP端口sock.listen(2)  # 监听端口,允许排队的个数为nconn, addr = sock.accept()  # 获取连接对象和地址while True:try:print("等待客户端发送数据...")data = conn.recv(1024)  # 接收文件数据if not data:breakprint("收到:", data.decode())# 电影id,评分movie_list,score_list, = internet.parse(data.decode())# 信息更新到数据库internet.receive(movie_list, score_list)# 建立用户向量,指定user用户分解矩阵user_vector=recommend.zip_user_vector(score_matrix,"user")# 获得k个预测结果movie_id=recommend.predict_movie_id(user_vector,predict_matrix,20)# 在电影表中查找信息movie_list=recommend.predict_movie(movie_id)# 电影信息发送给前端internet.send_recommendation(conn,movie_list)except:print("连接断开")

预测矩阵spark计算

该节是最体现大数据工作的地方,也是本次项目不得不用大数据框架之处,2k电影×8万用户的打分矩阵计算svd矩阵分解在个人电脑上会直接内存溢出,如果用原始数据想必服务器也遭不住,所以该方法一定需要分布式计算来负载压力。

同时该部分是我卡的最久的地方,首先spark语法不清楚,内部使用的数据结构不了解,每步执行都处在为什么报错和为什么能执行的疑惑中,如今结束了再次整理思路:
1,svd分解pyspark有现成的库,调库computeSVD即可实现,其中参数k表示取前k个奇异值
2,computeSVDRowMatrix的一个方法,故执行前要先将数据转为RowMatrix,但该结构又是一个分布式矩阵,在本地使用前要先收集一下,相关介绍如下:

RowMatrix是ApacheSparkMLlib库中的一个类,它表示一个分布式行矩阵。
它主要用于对大规模数据集进行线性代数操作,如奇异值分解(SVD)、主成分分析(PCA)等。RowMatrix的每行都是一个向量,并且这些向量可以分布式地存储在集群的不同节点上。
这使得它特别适合处理大型数据集,因为它允许在分布式环境中进行线性代数操作,而不需要将所有数据加载到单个节点上。

3,分解矩阵u,s,v分别是三种不同类型的矩阵,要分别处理保存,相关介绍如下:

    U:左奇异矩阵,类型为RowMatrix。它是一个分布式行矩阵,其中包含了SVD分解后的左奇异向量。由于它是一个分布式矩阵,因此无法直接像本地矩阵那样进行操作,但可以通过其方法(如rows)来访问其行数据。s:奇异值向量,类型为Vector。它是一个包含所有奇异值的向量,这些奇异值按照从大到小的顺序排列。这个向量是一个本地向量,可以直接在驱动程序中进行操作。V:右奇异矩阵,类型为Matrix。它是一个本地矩阵,包含了SVD分解后的右奇异向量。这个矩阵可以直接在驱动程序中进行操作,例如进行矩阵乘法或提取特定的列向量。

4,该过程计算量大,耗时长,执行前无比谨慎,别因为一点小问题白费一个小时的等待。

计算过程直接展示代码:

def caculate_svd_v(csv_path):start_time = time.time()print("正在加载spark...")conf = SparkConf().setAppName("svd").setMaster("local")sc = SparkContext(conf=conf)print("正在加载数据...")# 直接使用textFile的方式读取数据,然后再进行map操作raw_data = sc.textFile(csv_path)raw_data = raw_data.map(lambda x: x.split(","))matrix = RowMatrix(raw_data)print('矩阵构建完成')print('开始计算SVD...')# 是否计算U矩阵,压缩阶数k为15svd = matrix.computeSVD(15, computeU = True)cost_time=time.time()-start_timeprint("计算完成,耗时:",cost_time)print("正在转化矩阵为pandas...")df_u = row_to_df(svd.U)df_s=pd.DataFrame(svd.s.toArray())df_V=vector_to_df(svd.V)print("正在保存结果...")df_u.to_csv("E:\\software\\DataBase\\Data\\movie_data\\u.csv")df_s.to_csv("E:\\software\\DataBase\\Data\\movie_data\\s.csv")df_V.to_csv("E:\\software\\DataBase\\Data\\movie_data\\v.csv")

其中转化pandas过程中有两个函数需要自行编写,分别处理RowMatrixVector,代码展示如下:

def row_to_df(vector):# collect 方法将分布式向量转换为本地向量,然后将其转换为pandas数据框print("正在转化u矩阵...")# 返回一个列表u=vector.rows.collect()print("计算U矩阵规格为:",len(u),"*",len(u[0]),"=",len(u)*len(u[0]))return pd.DataFrame(u)
def vector_to_df(dense_matrix):# 用于将dense矩阵转换为pandas数据框print("获取到dense的值为:",dense_matrix)print("正在转化V矩阵...")# 获取行列数rows = dense_matrix.numRowscols = dense_matrix.numColsprint("计算V矩阵规格为:", rows, "*", cols,"=", rows * cols)value=dense_matrix.toArray()# 获得np值nplist=list(value)v_list=np.array(nplist).reshape(rows,cols)# 根据np值转为pandas数据框df=pd.DataFrame(v_list)print("获取到的pandas值为:",df)return df

这部分开始一直报错也是因为不了解其中的结构,要对矩阵s,u,v分别输出时发现总也不行,仔细看看会发现其实是类型问题,此时再根据类型去查解决方案即可,计算机中遇到的每个问题都有前人解决过,只要把问题问得够具体够详细,就没有解决不了的困难。

但该步仍有疑问没能解决:
1,单机如何实现性能扩展
spark负责分布式计算的资源调度和负载均衡,可以用集群的力量解决一个大问题,但本次实验中,spark采用单机部署,并没有其他计算机可供spark调度,它是如何实现大矩阵运算而不内存溢出呢?

2,文件读入方式不同导致崩溃
上述代码几乎重构过三次,第二次时我为了借助pandas库对标签过滤的方便,选择将数据先读入pandas,再由pandas传给RDD,到此都一切顺利,而等到计算svd时又会出现内存溢出错误。当时观察了RDD输出都和spark直接读取文件无异,其背后原因到目前尚不得解。

推荐流程

该部分实现四个函数:
1,计算svd的k阶压缩。spark加载计算较慢,处理后的V矩阵和用户矩阵可以用本机环境直接计算,故需自行编写该函数。
2,计算用户的特征向量。计算V矩阵时将隐向量设置为15,大意为15种电影类型,用户的标记也应该压缩到对15种电影的标记,该步使用svd分解压缩法实现。
3,计算预测矩阵并返回其中前k个最大值的电影id。V矩阵与特征向量做点积生成预测矩阵,因为只针对一个用户预测,故该矩阵其实是一个向量,在向量中选取前k个索引,到电影表中查找对应的电影id。(为了免去对indexcolumns的过滤处理,我单独设置了一张不含标签纯打分信息的表nolabel,该过程增加了磁盘开销,但减少了我的大脑开销)
4,根据电影id在电影表中查找电影信息。该步就比较简单了,用pd.loc索引查找电影id,相关电影信息加入列表即可。
四个函数的代码展示如下:

def get_svd(matrix,k):#U, sigma, VT = np.linalg.svd(matrix,full_matrices=False)# 主对角线数组值,其余均为0sigma=np.diag(sigma)# 截取前k个奇异值U_k=U[:, :k]sigma_k=sigma[:k, :k]VT_k=VT[:k, ]return U_k, sigma_k, VT_kdef zip_user_vector(file,user_id):# 负责将用户特征向量分解成3个矩阵,分别是U,S,VT,只用U作为用户的特征向量print("正在计算用户特征向量...")record=file.loc[user_id].valuesprint(record)print("正在分解用户矩阵")record=record.reshape(5, -1)print(record)u,s,v=get_svd(record,3)u=u.reshape(1,-1)print("分解后的矩阵格式为:", u.shape)print("分解后的矩阵为:", u)return udef predict_movie_id(user_matrix,predict_matrix,k):# 给出k部预测的电影idprint("正在预测用户评分...")matrix=np.dot(user_matrix,predict_matrix)# 将数组降序排列,返回数组的索引print("正在选择电影")sort_list=np.argsort(matrix)[::-1]# 选前k个最大值的索引index_list=sort_list[:k]# 获取电影id用于movie = pd.read_csv("E:\\software\\DataBase\\Data\\movie_data\\blank_ratings_matrix.csv", header=0, index_col=0)label = movie.columnsmovie_id=[]for i in index_list:movie_id.append(label[i])return movie_iddef predict_movie(movie_id):# 根据电影id在表中查找相关电影信息df=pd.read_csv("E:\\software\\DataBase\\Data\\movie_data\\new_movies.csv",header=0,index_col=0)movie_list=[]for i in movie_id:movie_list.append(df.loc[i])return movie_list

报文解析

该部分就两个功能:
1,将前端传来的数据解析成电影id和打分情况,写入打分表文件。
2,将后端生成的电影信息发送到前端。

直接展示代码:

def receive(movie_list,score_list):# 接受电影id和打分情况并写入文件file=pd.read_csv("E:\\software\\DataBase\\Data\\movie_data\\ratings_matrix.csv",header=0,index_col=0)for i in range(len(movie_list)):file.loc["user",movie_list[i]]=score_list[i]file.to_csv("E:\\software\\DataBase\\Data\\movie_data\\ratings_matrix.csv")def send_recommendation(conn,movie_list):for i in movie_list:conn.send(i)

总结

要说推荐系统也从属于机器学习?我是有点doubt的,它可一点没学全是我教的。不过就到现在的学习经历来说,计算机内的定义和区分并不十分严谨和界限分明,流传的很多说法甚至根本不是它的真正意思,只是因为它看起来是这样而一直被这么叫,所以实现之前不管对方是大佬还是小白,多问一嘴也是给自己省事。

该项目实现了简单的推荐系统,浅尝了一次推荐算法,再次搞了前后端的交互,要说读了研就是不一样,这项目含金量比起本科不知道高了多少,可跟实际情况比起来还是差很多,但毕竟只是个作业,了解到这一步我觉得已经极限了,对于刚入学两个月的我,做到这我认为已经极限了,对不一定当作主要方向,纯粹爱好驱动的尝试,也已经极限了。

总结与展望:
1,真正的大数据。不说上亿条数据,起码把整个数据集跑起来吧,这期间需要应该要学一些性能优化的方法。
2,改进的推荐算法,算法在实际应用中可能仍然不够实际,相关算法也有改进算法可以使用,甚至直接自己改进算法也不是不可能的。
3,优化安全的前后端。到目前前后端通信框架从来没使用过,安全性健壮性压根没考虑,只是单纯从实现角度完成,看现在行情都是要全栈,开发岗应该对整个流程都是要花点心思。

写在后面

前面的总结都是写于算法原理的初步探索阶段,在求知欲得到满足的快乐时期,真正实现起来,暂时想不到比一步一个坑更雅的说法。当时的很多小想法到现在都被磨平了,现在只觉得尽快实现比什么都重要,可以看到在我的代码中不乏简单粗暴和力大飞砖——反正电脑累总比我累强;另一方面也是期末时间紧任务重,不太有精力和心境去慢慢打磨作品了,再加上可能人知道的越多就知道自己有更多不知道的,到现在结束阶段只觉得项目实在粗糙,甚至像小孩过家家,不过还是不能站在当下嘲笑过去幼稚的自己,最近的话怎么说:那不是小丑,那是我的来时路,写csdn一方面算是做笔记,另一方面也算记录自己的成长之路。

相关文章:

pyspark实现基于协同过滤的电影推荐系统

最近在学一门大数据的课,课程要求很开放,任意做一个大数据相关的项目即可,不知道为什么我就想到推荐算法,一直到着手要做之前还没有新的更好的来代替,那就这个吧。 推荐算法 推荐算法的发展由来已久,但和…...

【VRChat 全身动捕】VIVE 手柄改 tracker 定位器教程,低成本光学动捕解决方案(持续更新中2024.11.26)

更新 0.0.1(2024/11/26): 1.解决了内建蓝牙无法识别、“steamVR 蓝牙不可用” 的解决方案 2.解决了 tracker 虽然建立了连接但是在 steamVR 界面上看不到的问题 3.解决了 VIVE 基站1.0 无法被蓝牙识别 && 无法被 steamVR 搜索到 &…...

C++11

C11 C11简介一.列表初始化{ }initializer_list 二.一些关键字三.右值引用和移动语义(重要)完美转发移动构造和移动赋值重载 四.小知识类成员变量初始化 五.可变参数模板六.lambda表达式(又称匿名函数)(重要&#xff09…...

MySQL - 表的增删查改

文章目录 1.新增1.1语法1.2单行插入1.3多行插入1.4插入后更新1.5替换 2.查找2.1语法2.2使用 3.修改3.1语法3.2使用 4.删除4.1语法4.2使用4.3截断表 5.插入查询结果5.1语法5.2使用 1.新增 1.1语法 INSERT [INTO] table_name [(column1, column2, ...)] VALUES (value1, value2…...

Vue3 调用子组件的方法和变量

1. 通过 ref 调用子组件的方法和变量 Vue 3 引入了 ref&#xff0c;你可以通过 ref 获取子组件实例&#xff0c;并调用其方法或访问其数据。 例子 子组件 (Child.vue) <template><div><p>{{ message }}</p><button click"updateMessage&qu…...

蓝桥杯嵌入式再学习(4)led的点亮

led的点亮的话先在cubemx里点一下配置 以下是对应的代码...

CentOS7安装nvm

CentOS7安装nvm 在 CentOS 7 上安装 NVM&#xff08;Node Version Manager&#xff09;可以通过以下步骤进行。NVM 是一个用于管理多个 Node.js 版本的工具&#xff0c;允许你在不同的项目中使用不同的 Node.js 版本。 步骤 1&#xff1a;更新系统 首先&#xff0c;确保你的…...

2024年工信部大数据分析师证书报考条件是怎样的?有什么用

大数据分析师&#xff0c;乃是这样一类专业人才&#xff0c;他们凭借着先进且高效的数据分析技术以及各类实用工具&#xff0c;对规模庞大、纷繁复杂的海量数据展开全面而细致的清洗、处理、分析以及解读工作。其工作的核心目标在于为企业的决策制定提供有力依据&#xff0c;推…...

天锐绿盾加密软件与Ping32联合打造企业级安全保护系统,确保敏感数据防泄密与加密管理

随着信息技术的飞速发展&#xff0c;企业在日常经营过程中产生和处理的大量敏感数据&#xff0c;面临着越来越复杂的安全威胁。尤其是在金融、医疗、法律等领域&#xff0c;数据泄漏不仅会造成企业巨大的经济损失&#xff0c;还可能破坏企业的信誉和客户信任。因此&#xff0c;…...

代码随想录算法训练营第六十天|Day60 图论

Bellman_ford 队列优化算法&#xff08;又名SPFA&#xff09; https://www.programmercarl.com/kamacoder/0094.%E5%9F%8E%E5%B8%82%E9%97%B4%E8%B4%A7%E7%89%A9%E8%BF%90%E8%BE%93I-SPFA.html 本题我们来系统讲解 Bellman_ford 队列优化算法 &#xff0c;也叫SPFA算法&#xf…...

Web登录页面设计

记录第一个前端界面&#xff0c;暑假期间写的&#xff0c;用了Lottie动画和canvas标签做动画&#xff0c;登录和注册也连接了数据库。 图片是从网上找的&#xff0c;如有侵权私信我删除&#xff0c;谢谢啦~...

Rust标准库中集合类型用法详解

文章目录 Vec<T> 动态数组创建动态数组增加删除元素访问元素遍历Vec控制容量修改元素元素排序 HashMap<K, V>哈希表创建 HashMap插入和更新元素访问元素删除元素遍历HashMap使用默认值 HashSet<T>哈希集合创建 HashSet插入和删除元素查找元素遍历HashSet使用…...

软件测试面试之数据库部分

1.取第 4 到5 条记录 --按ID从小到大&#xff0c;查询第到第条数据 select top4 *from(select top5 * from qicheorder by ID asc ) as TA order by ID desc--按ID从小到大&#xff0c;查询第到第条数据 select top 2*from(select top 4 *from qicheorder by ID asc )as TA o…...

Gitee markdown 使用方法(持续更新)

IPKISS 获取仿真器件的名称 引言正文标题换行第一种------在行末尾手动键入两个空格第二种------额外换行一次&#xff0c;即两行中间留一个空行 缩进与反缩进代码块行内代码添加图片添加超链接 加粗&#xff0c;倾斜&#xff0c;加粗倾斜 引言 有些保密性的文件或者教程&…...

水库大坝安全监测之量水堰计应用

量水堰计是水库大坝安全监测系统中的一种关键设备&#xff0c;主要用于测量水库水位、流量等水力参数。以下是量水堰计在水库大坝安全监测中的应用及注意事项&#xff1a; 一、量水堰计的工作原理 量水堰计是一种专门用于测量水流流量的仪器&#xff0c;其工作原理主要基于水流…...

Mouser EDI 需求分析

为了提高供应链的自动化水平&#xff0c;贸泽电子&#xff08;Mouser Electronics&#xff09;使用EDI技术更好地管理与其全球合作伙伴之间的业务数据往来。对接Mouser EDI&#xff0c;对于企业而言&#xff0c;需要在本地部署EDI软件&#xff0c;建立与Mouser之间的EDI连接通道…...

51单片机快速入门之中断的应用 2024/11/23 串口中断

51单片机快速入门之中断的应用 基本函数: void T0(void) interrupt 1 using 1 { 这里放入中断后需要做的操作 } void T0(void)&#xff1a; 这是一个函数声明&#xff0c;表明函数 T0 不接受任何参数&#xff0c;并且不返回任何值。 interrupt 1&#xff1a; 这是关键字和参…...

电脑显示器拔插DVI线后副屏不显示

问题&#xff1a;台式机副屏显示器插拔DVI线后副屏无法检测到&#xff0c;不显示 其他现象&#xff1a;电脑设备管理器“显示适配器”中只有独显&#xff0c;未显示集显。 尝试方法&#xff1a; 1、 重新插拔并拧紧DVI线、更换DVI线、将DVI线替换为VGA线、调换DVI线及VGA线两…...

git分支管理:release分支内容重置为master

背景&#xff1a;公司项目存在release和master分支&#xff0c;一直是release合并到master分支&#xff1b;由于历史总总原因导致release和master分支内容相差很大。某个夜晚&#xff0c;leader悄悄改了master分支的内容&#xff08;不走合并流程&#xff09;&#xff0c;然后通…...

python之poetry 安装、创建项目、修改源、创建虚拟环境等操作

CentOs7.5下安装python3&#xff0c;修改源&#xff0c;创建虚拟环境 python 虚拟环境的搭建 使用pyenv 管理多个版本的python 安装 pyenv curl https://pyenv.run | bash pyenv install 3.10 pyenv local 3.10 # 当前项目激活Python3.10 pyenv global 3.10 # 当前全部激活P…...

一个高度可扩展的 Golang ORM 库【GORM】

GORM 是一个功能强大的 Golang 对象关系映射&#xff08;ORM&#xff09;库&#xff0c;它提供了简洁的接口和全面的功能&#xff0c;帮助开发者更方便地操作数据库。 1. 完整的 ORM 功能 • 支持常见的关系模型&#xff1a; • Has One&#xff08;一对一&#xff09; • …...

hadoop_HA高可用

秒懂HA HA概述HDFS-HA工作机制工作要点元数据同步参数配置手动故障转移自动故障转移工作机制相关命令 YARN-HA参数配置自动故障转移机制相关命令 附录Zookeeper详解 HA概述 H(high)A(avilable)&#xff1a; 高可用&#xff0c;意味着必须有容错机制&#xff0c;不能因为集群故障…...

23种设计模式-工厂方法(Factory Method)设计模式

文章目录 一.什么是工厂方法设计模式&#xff1f;二. 工厂方法模式的特点三.工厂方法模式的结构四.工厂方法模式的优缺点五.工厂方法模式的 C 实现六.工厂方法模式的 Java 实现七.代码解析八.总结 类图&#xff1a; 工厂方法设计模式类图 一.什么是工厂方法设计模式&#xff1…...

论文笔记 SliceGPT: Compress Large Language Models By Deleting Rows And Columns

欲买桂花同载酒&#xff0c;终不似&#xff0c;少年游。 数学知识 秩&#xff1a; 矩阵中最大线性无关的行/列向量数。行秩与列秩相等。 线性无关&#xff1a;对于N个向量而言&#xff0c;如果任取一个向量 v \textbf{v} v&#xff0c;不能被剩下的N-1个向量通过线性组合的方式…...

构建 LLM (大型语言模型)应用程序——从入门到精通(第七部分:开源 RAG)

通过检索增强生成 (RAG) 应用程序的视角学习大型语言模型 (LLM)。 本系列博文 简介数据准备句子转换器矢量数据库搜索与检索大语言模型开源 RAG&#xff08;本帖&#xff09;评估服务LLM高级 RAG 1. 简介 我们之前的博客文章广泛探讨了大型语言模型 (LLM)&#xff0c;涵盖了其…...

快速理解倒排索引在ElasticSearch中的作用

一.基础概念 定义&#xff1a; 倒排索引是一种数据结构&#xff0c;用来加速文本数据的搜索和检索&#xff0c;和传统的索引方式不同&#xff0c;倒排索引会被每个词汇项与包含该词汇项的文档关联起来&#xff0c;从而去实现快速的全文检索。 举例&#xff1a; 在传统的全文…...

彻底理解微服务配置中心的作用

常见的配置中心有SpringCloudConfig、Apollo、Nacos等&#xff0c;理解它的作用&#xff0c;无非两点&#xff0c;一是配置中心能做什么&#xff0c;不使用配置中心会出现什么问题。 作用&#xff1a;配置中心是用来集中管理服务的配置&#xff0c;它是用来提高系统配置的维护…...

基于YOLOv8深度学习的智慧农业棉花采摘状态检测与语音提醒系统(PyQt5界面+数据集+训练代码)

智慧农业在现代农业中的应用日益广泛&#xff0c;其核心目标是通过智能化手段实现农业生产的自动化、精准化和高效化&#xff0c;而精准采摘技术作为智慧农业的重要组成部分&#xff0c;正受到越来越多的关注。棉花作为一种经济作物&#xff0c;其采摘过程传统上依赖于人工劳作…...

自动控制原理——BliBli站_DR_CAN

自动控制 2 稳定性分析 极点在左半平面 输入为单位冲击&#xff0c;而拉普拉斯变换为1&#xff1b;因此&#xff0c;开环和闭环系统&#xff0c;研究其传递函数的稳定性就可以了 2.5_非零初始条件下的传递函数_含有初始条件的传递函数 如果一个系统的初始条件不为0&#xff0…...

使用phpStudy小皮面板模拟后端服务器,搭建H5网站运行生产环境

一.下载安装小皮 小皮面板官网下载网址&#xff1a;小皮面板(phpstudy) - 让天下没有难配的服务器环境&#xff01; 安装说明&#xff08;特别注意&#xff09; 1. 安装路径不能包含“中文”或者“空格”&#xff0c;否则会报错&#xff08;例如错误提示&#xff1a;Cant cha…...

vue2 pinia 安装与入门

安装 本体 npm install pinia vue2特定 npm install vue/composition-api 持久化存本地 npm install pinia-plugin-persistvue.config.js 不加的话会报错 configureWebpack: { // 在configureWebpack里加上以下modulemodule: {rules: [{test: /\.mjs$/,include: /node_mo…...

路由传参、搜索、多选框勾选、新增/编辑表单复用

前言&#xff1a; 记录添加运动员页面功能的具体实现 ①由赛事管理页面跳转时路由传参&#xff08;携带该页面表格中莫某条数据对应的赛事id到另一个页面&#xff09;&#xff1b; ②搜索框实时搜索&#xff1b; ③多选框勾选搜索&#xff1b; ④新增表单和编辑表单复用&a…...

内网不出网上线cs

一:本地正向代理目标 如下&#xff0c;本地(10.211.55.2)挂好了基于 reGeorg 的 http 正向代理。代理为: Socks5 10.211.55.2 1080python2 reGeorgSocksProxy.py -l 0.0.0.0 -p 1080 -u http://10.211.55.3:8080/shiro/tunnel.jsp 二&#xff1a;虚拟机配置proxifer 我们是…...

防止按钮被频繁点击

在做开发的时候,不希望按钮被用户频繁点击,给后端服务器增加负担,这个时候,可以在按钮的触发函数加上如下代码: // 禁用按钮 const fetchButton document.querySelector(.btn-fetch); fetchButton.disabled true; // 延时61秒后重新启用按钮 setTimeout(() > { fetchBut…...

Zariski交换代数经典教材Commutative Algebra系列(pdf可复制版)

Zariski的名字估计学代数几何的人都耳熟能详&#xff0c;先是入门时期的交换代数教材&#xff0c;然后就是深入研究时期随处可见的Zariski拓扑。本帖我们分享的便是著名的Zariski交换代数教材。 Oscar Zariski & Pierre Samuel写的交换代数经典教材Commutative Algebra&am…...

【C++】数据类型(上)

C规定在创建一个变量或一个常量时&#xff0c;必须要指定出相应的数据类型&#xff0c;否则无法给变量分配内存 数据类型存在意义&#xff1a;给变量分配合适的内存空间。 1.1 整型 整型作用&#xff1a;整型变量表示的整数类型的数据。 C中能够表示整型类型的有以下几种…...

(C语言) 8大翻译阶段

(C语言) 8大翻译阶段 文章目录 (C语言) 8大翻译阶段⭐前言&#x1f5c3;️8大阶段&#x1f5c2;️1. 字符映射&#x1f5c2;️2. 行分割&#x1f5c2;️3. 标记化&#x1f5c2;️4. 预处理&#x1f5c2;️5. 字符集映射&#x1f5c2;️6. 字符串拼接&#x1f5c2;️7. 翻译&…...

QT QHBoxLayout控件 全面详解

本系列文章全面的介绍了QT中的57种控件的使用方法以及示例,包括 Button(PushButton、toolButton、radioButton、checkBox、commandLinkButton、buttonBox)、Layouts(verticalLayout、horizontalLayout、gridLayout、formLayout)、Spacers(verticalSpacer、horizontalSpacer)、…...

生成式 AI 应用创新引擎dify.ai部署并集成ollama大模型详细图文教程

引言 Dify 是一个开源 LLM 应用开发平台&#xff0c;拥有直观的界面结合了 AI 工作流、RAG 管道、代理功能、模型管理、可观察性功能等&#xff0c;可以快速从原型开发到生产。 Ollama 是一个本地推理框架&#xff0c;允许开发人员轻松地在本地部署和运行 LLM&#xff…...

MySQL闪回恢复:轻松应对数据误删,数据安全有保障

在数据库管理中&#xff0c;数据误删是一个常见且棘手的问题。传统的数据恢复方法可能涉及复杂的操作&#xff0c;如全量备份和增量备份的恢复。MySQL的闪回恢复功能提供了一种更为简便、高效的数据恢复手段。本文将详细介绍MySQL闪回恢复的原理、配置和使用方法&#xff0c;帮…...

面经-综合面/hr面

面经-综合面/hr面 概述1.大学期间遇到的困难&#xff0c;怎么解决的2. 大学期间印象最深/最难忘的是什么3. 大学里面担任了什么职务没&#xff1f;做了什么工作&#xff1f;4. 大学最大的遗憾是什么&#xff1f;5. 对自己的未来规划6. 对自己的评价7. 自己的优缺点8. 对公司的认…...

利用Java爬虫获取阿里巴巴中国站跨境属性的详细指南

在全球化贸易的浪潮中&#xff0c;跨境电商正成为连接全球买家和卖家的重要桥梁。阿里巴巴中国站作为全球领先的B2B电子商务平台&#xff0c;提供了海量的商品信息&#xff0c;其中跨境属性信息对于跨境电商尤为重要。本文将详细介绍如何使用Java编写爬虫&#xff0c;从阿里巴巴…...

Vue3.0 所采用的 Composition Api 与 Vue2.x 使用的 Options Api 有什么不同?

Vue 3.0 引入的 Composition API 和 Vue 2.x 中的 Options API 是两种不同的开发方式。它们在组织和管理组件代码的方式上有很大的差异。虽然两者都能实现相同的功能,但 Composition API 提供了更好的灵活性和可维护性,特别是对于复杂的组件和大型项目。 以下是对比这两种 A…...

电脑中的vcruntime140_1.dll文件有问题要怎么解决?一键修复vcruntime140_1.dll

遇到“vcruntime140_1.dll无法继续执行代码”的错误通常表明电脑中的vcruntime140_1.dll文件有问题。这个文件属于Visual C Redistributable&#xff0c;对很多程序的运行至关重要。本文将提供几个步骤&#xff0c;帮助你迅速修复这一错误&#xff0c;使电脑恢复正常工作状态。…...

【网络安全 | 漏洞挖掘】绕过SAML认证获得管理员面板访问权限

未经许可,不得转载。 文章目录 什么是SAML认证?SAML是如何工作的?SAML响应结构漏洞结果什么是SAML认证? SAML(安全断言标记语言)用于单点登录(SSO)。它是一种功能,允许用户在多个服务之间切换时无需多次登录。例如,如果你已经登录了facebook.com,就不需要再次输入凭…...

技能之发布自己的依赖到npm上

目录 开始 解决 步骤一&#xff1a; 步骤二&#xff1a; 步骤三&#xff1a; 运用 一直以为自己的项目在github上有了&#xff08;之传了github&#xff09;就可以进行npm install下载&#xff0c;有没有和我一样萌萌的同学。没事&#xff0c;萌萌乎乎的不犯罪。 偶然的机…...

python+django5.1+docker实现CICD自动化部署springboot 项目前后端分离vue-element

一、开发环境搭建和配置 # channels是一个用于在Django中实现WebSocket、HTTP/2和其他异步协议的库。 pip install channels#channels-redis是一个用于在Django Channels中使用Redis作为后台存储的库。它可以用于处理#WebSocket连接的持久化和消息传递。 pip install channels…...

GitHub仓库的README添加视频播放链接和压缩视频网站推荐

想在github的README里面添加视频&#xff0c;有限制&#xff0c;不能超过10MB&#xff0c;所以需要压缩&#xff0c;推荐一个超级厉害的压缩工具&#xff0c;我一个30的视频最后压缩到了3M不到&#xff0c;而且清晰度没有变化&#xff0c;视频压缩网站地址&#xff1a;在线减小…...

PHP学习_1

一.PHP基本语法 1.变量 <?php header("Content-type:text/html;charsetutf-8"); $name1"yanxiao"; $age"3";echo "$name1 今年 $age <br>"; echo "$name1 喜欢 xtf <br>"; echo "$name1 不喜欢英语&…...

深入解析 MySQL 启动方式:`systemctl` 与 `mysqld` 的对比与应用

目录 前言1. 使用 systemctl 启动 MySQL1.1 什么是 systemctl1.2 systemctl 启动 MySQL 的方法1.3 应用场景1.4 优缺点优点缺点 2. 使用 mysqld 命令直接启动 MySQL2.1 什么是 mysqld2.2 mysqld 启动 MySQL 的方法2.3 应用场景2.4 优缺点优点缺点 3. 对比分析结语 前言 MySQL …...