16. BI - 推荐系统之 ALS 实现

本文为 「茶桁的 AI 秘籍 - BI 篇 第 16 篇」

茶桁的AI秘籍_核心BI_16


Hi,你好。我是茶桁。

前面两节课的内容中,我们从矩阵分解到 ALS 原理,依次给大家讲解了推荐系统中的一个核心概念。

矩阵分解中拆矩阵的背后其实是聚类。就说 k 等于几是人工设定的,所以跟聚类概念很像。就是要把人群划分成几类,把电影划成几类。k 等于 3 是自己去设定的,也可以把它拆成 k 等于 4、k 等于 5,都是一样的,是要完成聚类任务。

聚类不需要操心到底有哪些类型,它会自动的聚成这几类。这也是为什么把它称为隐分类。

「隐」就是我们知道它聚成了三种类型,但是不太清楚这三种类型具体的名称应该叫什么。所以它确实用了聚类的概念,至于为什么用了聚类概念,是因为最后学出来的类型是在 3 个维度上打分的,一个用户有 3 个维度的评分,一个商品也有 3 个维度评分。其实就相当于是把用户聚成了三类,商品聚成了三类。

或者换个角度,可以看一下

image-4

还是用之前的 12 * 9 的矩阵,以前一个用户的向量有 9 个维度,9 个维度还是比较多的,有可能每个维度不全。现在我们要把 9 个维度做成 3 个维度(k=3),这 3 个维度就变成了更稠密的压缩的维度,这个小的维度就是个降维。

所以用户要降成 3 个维度,商品也要降成相同的 3 个维度,它的概念都是一样的,大家都是在这个维度上面做一个抽象,这是为什么最开始我们给它画这个场景。用户商品中间这个隐分类我们把它称为 interest。

或者这个 k 就是个聚类方式。用户聚成了几种兴趣,那么商品也是聚成了相同的几种兴趣,大家都是在相同的这样的 3 个属性的维度上面去打分。用户的打分如果在 3 个属性上高那就代表你喜欢,商品上面高就代表这个类型的很强。

在这里插入图片描述

咱们来举个例子:一个用户的特效片是高的,封神第一部它在特效片上也是高的,剧情片和动画片它没有分吗?并不是,它也有分,也会有相关的一个分数。这样我们再进行计算的时候就更好的来进行计算了,就相当于把一个大维度降成了小维度。

那之前这些整个的就是给大家讲解的矩阵分解原理,原理还是要明白的。虽然最后工作的时候大部分内容都是调包,但是在这之前还是很有必要去了解一下它的原理。有些时候有可能一是面试的时候问,第二你调包的时候更知道它为什么要用到这个包,这个包背后逻辑是什么逻辑。

那这个包怎么调?有两种方式来调,一种就是大数据的方法,用 Spark。

Spark 是个大数据的一个平台,在 Spark 里面我们默认它是使用了一个 ALS。Spark 如果你未来感兴趣可以看一看,它有两个机器学习的库,一个叫 mllib 库,一个叫 ml 库。建议大家直接用 ml 库,也是官方推荐的。

mllib 库已经废弃掉了,3.0 版本之后不再维护了。所以大家直接用 ml 这个工具箱就 OK 了。两个一个很大的区别就是 ml 主要操作的是 DataFrame, mllib 操作的是 RDD,两个面向的数据集是不一样的。相较而言,ml 在 DataFrame 上的抽象级别更高,数据和操作耦合度更低。

第二点它使用起来已经做了更好的封装,就更像 sklearn。机器学习中大家都习惯用 sklearn,最开始进入机器学习也是调这个包,所以它就更像 sklearn 的接口使用起来衔接起来更顺畅一点。

如果你用 Python 代码,那也该大家找了一个在 Github 上的,有一个 ALS 类,可以直接引用这个类:

https://github.com/tushushu/imylu/blob/master/imylu/recommend/als.py

对 MovieLens 进行电影推荐

来看一下下面这个例子,数据集是 MovieLens,可以在这里去下载:https://www.kaggle.com/jneupane12/movielens/download

MovieLens 是一个电影的评分网站,上面有几十万的电影和很多的人的一些打分。

20231216172715

我们以其中一个人为例,他给这么多电影打了一些分数。整个数据集叫readings.csv,有四个字段,userId, movieId, rating 和 timestamp。

我们现在看一看这个矩阵,一个人打了这么多电影分数,那你觉得如果把它看成一个非常大的一个矩阵的话,这个矩阵是稀疏的还是稠密的呢?

虽然用户 1 给这么多电影打分,但实际上要知道电影其实会很多,可能有 10 万部,而你只打了 1,000 部,所以它还是个稀疏的。我们就需要猜出来那些没有打分的,比如说中间那些 id 为 3、4、28,这些都没有打分。

怎么做呢?我们来看看代码。这里就是封装好的一个类,它是一个矩阵的概念。里面具体代码大家可以去我的代码仓库里去查看源码。

它就是做了一个矩阵的相乘,还有矩阵的转质啊等等。中间都封装好了矩阵的乘法,封装了 ALS 类,后面使用过程中我们就直接调包就好了。

class Matrix(object):
def __init__(self, data):
    self.data = data
    self.shape = (len(data), len(data[0]))

def row(self, row_no):
    return Matrix([self.data[row_no]])

def col(self, col_no):
    m = self.shape[0]
    return Matrix([[self.data[i][col_no]] for i in range(m)])

@property
def is_square(self):
    return self.shape[0] == self.shape[1]

@property
def transpose(self):
    data = list(map(list, zip(*self.data)))
    return Matrix(data)

def _eye(self, n):
    return [[0 if i != j else 1 for j in range(n)] for i in range(n)]

@property
def eye(self):
    assert self.is_square, "The matrix has to be square!"
    data = self._eye(self.shape[0])
    return Matrix(data)

def _gaussian_elimination(self, aug_matrix):
    n = len(aug_matrix)
    m = len(aug_matrix[0])

    # From top to bottom.
    for col_idx in range(n):
        # Check if element on the diagonal is zero.
        if aug_matrix[col_idx][col_idx] == 0:
            row_idx = col_idx
            # Find a row whose element has same column index with
            # the element on the diagonal is not zero.
            while row_idx < n and aug_matrix[row_idx][col_idx] == 0:
                row_idx += 1
            # Add this row to the row of the element on the diagonal.
            for i in range(col_idx, m):
                aug_matrix[col_idx][i] += aug_matrix[row_idx][i]

        # Elimiate the non-zero element.
        for i in range(col_idx + 1, n):
            # Skip the zero element.
            if aug_matrix[i][col_idx] == 0:
                continue
            # Elimiate the non-zero element.
            k = aug_matrix[i][col_idx] / aug_matrix[col_idx][col_idx]
            for j in range(col_idx, m):
                aug_matrix[i][j] -= k * aug_matrix[col_idx][j]

    # From bottom to top.
    for col_idx in range(n - 1, -1, -1):
        # Elimiate the non-zero element.
        for i in range(col_idx):
            # Skip the zero element.
            if aug_matrix[i][col_idx] == 0:
                continue
            # Elimiate the non-zero element.
            k = aug_matrix[i][col_idx] / aug_matrix[col_idx][col_idx]
            for j in chain(range(i, col_idx + 1), range(n, m)):
                aug_matrix[i][j] -= k * aug_matrix[col_idx][j]

    # Iterate the element on the diagonal.
    for i in range(n):
        k = 1 / aug_matrix[i][i]
        aug_matrix[i][i] *= k
        for j in range(n, m):
            aug_matrix[i][j] *= k

    return aug_matrix

def _inverse(self, data):
    n = len(data)
    unit_matrix = self._eye(n)
    aug_matrix = [a + b for a, b in zip(self.data, unit_matrix)]
    ret = self._gaussian_elimination(aug_matrix)
    return list(map(lambda x: x[n:], ret))

@property
def inverse(self):
    assert self.is_square, "The matrix has to be square!"
    data = self._inverse(self.data)
    return Matrix(data)

def _row_mul(self, row_A, row_B):
    return sum(x[0] * x[1] for x in zip(row_A, row_B))

def _mat_mul(self, row_A, B):
    row_pairs = product([row_A], B.transpose.data)
    return [self._row_mul(*row_pair) for row_pair in row_pairs]

def mat_mul(self, B):
    error_msg = "A's column count does not match B's row count!"
    assert self.shape[1] == B.shape[0], error_msg
    return Matrix([self._mat_mul(row_A, B) for row_A in self.data])

def _mean(self, data):
    m = len(data)
    n = len(data[0])
    ret = [0 for _ in range(n)]
    for row in data:
        for j in range(n):
            ret[j] += row[j] / m
    return ret

def mean(self):
    return Matrix(self._mean(self.data))

def scala_mul(self, scala):
    m, n = self.shape
    data = deepcopy(self.data)
    for i in range(m):
        for j in range(n):
            data[i][j] *= scala
    return Matrix(data)

然后我们来创建一个读取数据的方法:

def load_movie_ratings(file):
with open(file) as ff:
    lines = iter(ff)
    col_names = ", ".join(next(lines)[:-1].split(",")[:-1])
    data = [[float(x) if i == 2 else int(x)
            for i, x in enumerate(line[:-1].split(",")[:-1])]
        for line in lines]
return data

接着,就是要写一个 ALS 的实现

class ALS(object):
def __init__(self):
    self.user_ids = None
    self.item_ids = None
    self.user_ids_dict = None
    self.item_ids_dict = None
    self.user_matrix = None
    self.item_matrix = None
    self.user_items = None
    self.shape = None
    self.rmse = None


def _process_data(self, X):
    self.user_ids = tuple((set(map(lambda x: x[0], X))))
    self.user_ids_dict = dict(map(lambda x: x[::-1], enumerate(self.user_ids)))

    self.item_ids = tuple((set(map(lambda x: x[1], X))))
    self.item_ids_dict = dict(map(lambda x: x[::-1], enumerate(self.item_ids)))

    self.shape = (len(self.user_ids), len(self.item_ids))

    ratings = defaultdict(lambda: defaultdict(int))
    ratings_T = defaultdict(lambda: defaultdict(int))
    for row in X:
        user_id, item_id, rating = row
        ratings[user_id][item_id] = rating
        ratings_T[item_id][user_id] = rating

    err_msg = "Length of user_ids %d and ratings %d not match!" % (len(self.user_ids), len(ratings))
    assert len(self.user_ids) == len(ratings), err_msg

    err_msg = "Length of item_ids %d and ratings_T %d not match!" % (len(self.item_ids), len(ratings_T))
    assert len(self.item_ids) == len(ratings_T), err_msg
    return ratings, ratings_T


def _users_mul_ratings(self, users, ratings_T):
    def f(users_row, item_id):
        user_ids = iter(ratings_T[item_id].keys())
        scores = iter(ratings_T[item_id].values())
        col_nos = map(lambda x: self.user_ids_dict[x], user_ids)
        _users_row = map(lambda x: users_row[x], col_nos)
        return sum(a * b for a, b in zip(_users_row, scores))

    ret = [[f(users_row, item_id) for item_id in self.item_ids] for users_row in users.data]
    return Matrix(ret)

def _items_mul_ratings(self, items, ratings):
    def f(items_row, user_id):
        item_ids = iter(ratings[user_id].keys())
        scores = iter(ratings[user_id].values())
        col_nos = map(lambda x: self.item_ids_dict[x], item_ids)
        _items_row = map(lambda x: items_row[x], col_nos)
        return sum(a * b for a, b in zip(_items_row, scores))

    ret = [[f(items_row, user_id) for user_id in self.user_ids] for items_row in items.data]
    return Matrix(ret)

# 生成随机矩阵
def _gen_random_matrix(self, n_rows, n_colums):
    data = np.random.rand(n_rows, n_colums)
    return Matrix(data)


# 计算 RMSE
def _get_rmse(self, ratings):
        m, n = self.shape
        mse = 0.0
        n_elements = sum(map(len, ratings.values()))
        for i in range(m):
            for j in range(n):
                user_id = self.user_ids[i]
                item_id = self.item_ids[j]
                rating = ratings[user_id][item_id]
                if rating > 0:
                    user_row = self.user_matrix.col(i).transpose
                    item_col = self.item_matrix.col(j)
                    rating_hat = user_row.mat_mul(item_col).data[0][0]
                    square_error = (rating - rating_hat) ** 2
                    mse += square_error / n_elements
        return mse ** 0.5

# 模型训练
def fit(self, X, k, max_iter=10):
    ratings, ratings_T = self._process_data(X)
    self.user_items = {k: set(v.keys()) for k, v in ratings.items()}
    m, n = self.shape

    error_msg = "Parameter k must be less than the rank of original matrix"
    assert k < min(m, n), error_msg

    self.user_matrix = self._gen_random_matrix(k, m)

    for i in range(max_iter):
        if i % 2:
            items = self.item_matrix
            self.user_matrix = self._items_mul_ratings(
                items.mat_mul(items.transpose).inverse.mat_mul(items),
                ratings
            )
        else:
            users = self.user_matrix
            self.item_matrix = self._users_mul_ratings(
                users.mat_mul(users.transpose).inverse.mat_mul(users),
                ratings_T
            )
        rmse = self._get_rmse(ratings)
        print("Iterations: %d, RMSE: %.6f" % (i + 1, rmse))

    self.rmse = rmse

# Top-n 推荐,用户列表:user_id, n_items: Top-n
def _predict(self, user_id, n_items):
    users_col = self.user_matrix.col(self.user_ids_dict[user_id])
    users_col = users_col.transpose

    items_col = enumerate(users_col.mat_mul(self.item_matrix).data[0])
    items_scores = map(lambda x: (self.item_ids[x[0]], x[1]), items_col)
    viewed_items = self.user_items[user_id]
    items_scores = filter(lambda x: x[0] not in viewed_items, items_scores)

    return sorted(items_scores, key=lambda x: x[1], reverse=True)[:n_items]

# 预测多个用户
def predict(self, user_ids, n_items=10):
    return [self._predict(user_id, n_items) for user_id in user_ids]

那接着就是写一个主函数来对方法进行调用,并且开始进行学习。第一个我们创建好这个 model

model = ALS()

然后进行加载数据

data = load_movie_ratings('./ratings.csv')

load_movie_ratings是已经封装好的一个读取数据的方法。这个方法做的事情就是导入数据并返回一个矩阵。

然后就是进行 fit 原来加载好的数据

model.fit(data, k=3, max_iter=2)

k 等于 3 代表聚类个数,max_iter 设置的迭代次数很少,因为他计算的这个速度会比较慢,为了方便的话就只计算两轮。两轮之后就会有结果,把这个结果做一个预测,我们想要给用户 1 到 12 来做预测,推荐两个商品。predict 给用户 1 到 12 推两个商品,然后把商品结果打印出来。

在这里插入图片描述

可以一起来看一看,我们的结果中第一轮和第二轮结果,MSE 结果都出来了。那这个 RMSE 代表什么含义?R 就是开了一个平方,所以它是在原有基础上开了根号。

第一轮我们得到 3.35, 一共学了两轮,学两轮基本上还没有学好,可以看一看第二轮 MSE 其实已经变得比较小了,为 0.31。整体的打分其实都不高,截图中是给用户推荐的一个结果,包含了商品 id。

这是第一个,我们用 ALS 可以去完成这样一个任务完成推荐,用 Python 包。还可以用 Spark,可以使用 ml 以及 mllib 去完成,大家可以安装一个 pyspark 来进行调用。

不过我的 M1 一直没有调好 Spark 环境,所以这一段演示也就暂时没办法拿给大家了,虽然代码在,但是因为没有环境跑过,所以可行性也不太清楚,就不放出来了,万一错了就是误导大家。

那下一节课呢,我会给大家再介绍一个方法,咱们下节课再见。

  • 48
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
### 回答1: Spark ALS协同过滤推荐系统代码实现的步骤如下: 1. 数据准备:将用户评分数据转换为Spark RDD格式,其中每个元素包含用户ID、物品ID和评分值。 2. 模型训练:使用Spark MLlib中的ALS算法训练模型,设置模型参数,如迭代次数、正则化参数等。 3. 模型评估:使用RMSE或MAE等指标评估模型的性能。 4. 推荐生成:使用训练好的模型,为每个用户生成推荐物品列表。 5. 结果展示:将推荐结果保存到数据库或文件中,并展示给用户。 具体的代码实现可以参考Spark官方文档或相关教程。 ### 回答2: 协同过滤是一种推荐算法,它通过分析用户的行为、偏好和历史数据,找出用户之间的相似性和相似喜好,然后推荐相似用户喜欢的物品。Spark ALS是一种流行的协同过滤推荐算法,它使用ALS(交替最小二乘)来学习用户和物品的潜在特征,并推荐最有可能喜欢的物品。 实现Spark ALS协同过滤推荐系统需要一定的代码实现。以下为实现步骤: 1.准备数据集:首先需要准备一个数据集,包括用户、物品和评分数据。数据集可以来自互联网、数据库或其他资源。数据集需要转换为RDD,然后将其拆分成训练集和测试集。 2.构建模型:使用Spark MLlib的ALS算法构建协同过滤模型。ALS算法需要配置参数,如潜在因子的数量、正则化参数、迭代次数等。模型还需要训练数据集,学习用户和物品的潜在特征,并估计评分。 3.评估模型:评估模型的性能、准确度和可靠性。使用测试集评估模型对新数据的预测能力,计算均方差和平均绝对误差等指标,来评估模型的优劣。 4.应用模型:最后,使用训练好的模型对新用户和物品进行推荐。通过查找相似用户和物品,并预测他们的评分、喜好,给用户推荐最可能感兴趣的物品。 以上是基于Spark ALS协同过滤推荐系统的代码实现步骤。虽然这个过程可能需要一些学习和经验,但使用Spark ALS协同过滤推荐系统还是相对简单的。实现这个模型可以帮助我们发现用户喜好、增加用户粘性、提高销售额和品牌忠诚度等。 ### 回答3: Apache Spark ALS(Alternating Least Squares)是基于矩阵分解的协同过滤推荐算法,其能够准确地预测用户对物品的评分。在实现ALS推荐系统时,需要以下几个步骤: 1. 数据预处理:将用户物品评分数据转换为Spark能够处理的Rating对象。Rating对象通常由三个属性组成:用户ID、物品ID和评分值。 2. 切分数据集:将数据集划分为训练集和测试集。通常是将80%的数据作为训练集,20%的数据作为测试集。 3. ALS模型训练:使用ALS算法从训练集中训练一个模型。训练模型时需要设置参数,如rank、iterations、lambda等等。 4. 评价模型性能:使用测试集对模型进行评价,可以使用RMSE、MAE等指标来评价预测的准确性。 5. 使用模型进行推荐:通过模型预测用户未评分物品的评分值,并根据评分值排序推荐物品给用户。 以下是具体的代码实现: 1. 数据预处理 ```scala import org.apache.spark.ml.recommendation.ALS import org.apache.spark.sql.functions._ // 读取数据 val ratings = spark.read.format("csv") .option("header", "true") .load("ratings.csv") .selectExpr("cast(userId as int) userId", "cast(movieId as int) movieId", "cast(rating as float) rating", "cast(timestamp as long) timestamp") // 转换为Rating对象 val training = ratings.rdd.map(row => org.apache.spark.ml.recommendation.Rating(row.getInt(0), row.getInt(1), row.getFloat(2))) ``` 2. 切分数据集 ```scala val Array(train, test) = training.randomSplit(Array(0.8, 0.2)) ``` 3. ALS模型训练 ```scala // 训练ALS模型 val als = new ALS() .setMaxIter(10) .setRegParam(0.1) .setRank(10) .setUserCol("userId") .setItemCol("movieId") .setRatingCol("rating") val model = als.fit(train) ``` 4. 评价模型性能 ```scala import org.apache.spark.ml.evaluation.RegressionEvaluator // 在测试集上进行评价 val predictions = model.transform(test) val evaluator = new RegressionEvaluator() .setMetricName("rmse") .setLabelCol("rating") .setPredictionCol("prediction") val rmse = evaluator.evaluate(predictions) println(s"Root-mean-square error = $rmse") ``` 5. 使用模型进行推荐 ```scala // 推荐 val recommendations = model.recommendForAllUsers(10) // 将结果存储到文件中 recommendations.write.format("csv") .option("header", "true") .save("output") ``` 以上就是使用Spark ALS实现协同过滤推荐系统的详细步骤和代码实现。Spark ALS推荐算法在海量数据上具有高效性和准确性,能够大大提高推荐系统的性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

茶桁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值