动手学机器学习双线性模型+习题

news2024/11/23 18:52:01

在数学中,双线性的含义为,二元函数固定任意一个自变量时,函数关于另一个自变量线性

矩阵分解

设想有N个用户和M部电影,构建一个用户画像库,包含每个用户更偏好哪些类型的特征,以及偏好的程度。假设特征的个数是d,那么所有电影的特征构成的矩阵是P∈R^Mxd,用户喜好构成的矩阵是Q∈R^Nxd

 最后,用这两个矩阵的乘积 R = P.T * Q 可以还原出用户对电影的评分。即使用户对某部电影并没有打分,我们也能通过矩阵乘积,根据用户喜欢的特征和该电影具有的特征,预测出用户对电影的喜好程度

实际上,我们通常能获取到的并不是P和Q,而是打分的结果R。并且由于一个用户只会对极其有限的一部分电影打分,矩阵R是非常稀疏的,绝大多数元素都是空白。因此,我们需要从R有限的元素中推测出用户的喜好P和电影的特征Q

 变成MSE形式+正则化得到:

这里的正则化不是针对整个矩阵,而是每一行,因为电影之间、用户之间是相互独立的

对p和q的梯度:

 

动手实现矩阵分解

import numpy as np
import matplotlib.pyplot as plt
from tqdm import tqdm # 进度条工具

data = np.loadtxt('movielens_100k.csv', delimiter=',', dtype=int)
print('数据集大小:', len(data))
# 用户和电影都是从1开始编号的,我们将其转化为从0开始
data[:, :2] = data[:, :2] - 1

# 计算用户和电影数量
users = set()
items = set()
for i, j, k in data:
    users.add(i)
    items.add(j)
user_num = len(users)
item_num = len(items)
print(f'用户数:{user_num},电影数:{item_num}')

# 设置随机种子,划分训练集与测试集
np.random.seed(0)

ratio = 0.8
split = int(len(data) * ratio)
np.random.shuffle(data)
train = data[:split]
test = data[split:]

# 统计训练集中每个用户和电影出现的数量,作为正则化的权重
user_cnt = np.bincount(train[:, 0], minlength=user_num)
item_cnt = np.bincount(train[:, 1], minlength=item_num)
print(user_cnt[:10])
print(item_cnt[:10])

# 用户和电影的编号要作为下标,必须保存为整数
user_train, user_test = train[:, 0], test[:, 0]
item_train, item_test = train[:, 1], test[:, 1]
y_train, y_test = train[:, 2], test[:, 2]

用set去重

user_cnt用np.bincount计算数组 train 中第一列中每个元素出现的次数

class MF:
    def __init__(self, N, M, d):
        # N是用户数量,M是电影数量,d是特征维度
        # 定义模型参数
        self.user_params = np.ones((N, d))
        self.item_params = np.ones((M, d))

    def pred(self, user_id, item_id):
        # 预测用户user_id对电影item_id的打分
        # 获得用户偏好和电影特征
        user_param = self.user_params[user_id]
        item_param = self.item_params[item_id]
        # 返回预测的评分
        rating_pred = np.sum(user_param * item_param, axis=1)
        return rating_pred

    def update(self, user_grad, item_grad, lr):
        # 根据参数的梯度更新参数
        self.user_params -= lr * user_grad
        self.item_params -= lr * item_grad

给定用户 ID 和电影 ID,计算用户参数和电影参数的乘积,并返回预测的评分

def train(model, learning_rate, lbd, max_training_step, batch_size):
    train_losses = []
    test_losses = []
    batch_num = int(np.ceil(len(user_train) / batch_size))
    with tqdm(range(max_training_step * batch_num)) as pbar:
        for epoch in range(max_training_step):
            # 随机梯度下降
            train_rmse = 0
            for i in range(batch_num):
                # 获取当前批量
                st = i * batch_size
                ed = min(len(user_train), st + batch_size)
                user_batch = user_train[st: ed]
                item_batch = item_train[st: ed]
                y_batch = y_train[st: ed]
                # 计算模型预测
                y_pred = model.pred(user_batch, item_batch)
                # 计算梯度
                P = model.user_params
                Q = model.item_params
                errs = y_batch - y_pred
                P_grad = np.zeros_like(P)
                Q_grad = np.zeros_like(Q)
                for user, item, err in zip(user_batch, item_batch, errs):
                    P_grad[user] = P_grad[user] - err * Q[item] + lbd * P[user]
                    Q_grad[item] = Q_grad[item] - err * P[user] + lbd * Q[item]
                model.update(P_grad / len(user_batch), Q_grad / len(user_batch), learning_rate)

                train_rmse += np.mean(errs ** 2)
                # 更新进度条
                pbar.set_postfix({
                    'Epoch': epoch,
                    'Train RMSE': f'{np.sqrt(train_rmse / (i + 1)):.4f}',
                    'Test RMSE': f'{test_losses[-1]:.4f}' if test_losses else None
                })
                pbar.update(1)

            # 计算测试集上的RMSE
            train_rmse = np.sqrt(train_rmse / len(user_train))
            train_losses.append(train_rmse)
            y_test_pred = model.pred(user_test, item_test)
            test_rmse = np.sqrt(np.mean((y_test - y_test_pred) ** 2))
            test_losses.append(test_rmse)

    return train_losses, test_losses

np.ceil 用于计算数组中每个元素的向上取整值

遍历训练集中的每个批量,进行随机梯度下降训练

根据梯度公式求梯度,并根据批大小调整

通过 set_postfix 方法设置进度条的附加信息,在更新完附加信息后,使用 pbar.update(1) 更新进度条,使其前进一步

pred的结果:也差不多相差1

因子分解机

FM 的应用场景与 MF 有一些区别,MF 的目标是从交互的结果中计算出用户和物品的特征;而 FM 则正好相反,希望通过物品的特征和某个用户点击这些物品的历史记录,预测该用户点击其他物品的概率,即点击率(click through rate,CTR)

由于被点击和未被点击是一个二分类问题,CTR 预估可以用逻辑斯谛回归模型来解决,然而逻辑回归的线性参数化假设中不同的特征xi与xj之间并没有运算,因此需要进一步引入双线性部分:

写成向量形式:

物体的特征向量one-hot后会过于稀疏,y(x)对w求导后得到的xixj大多地方都是0,所以难以对wij更新

从该结果中可以看出,只要xs≠0,参数的梯度vs就不为零,可以用梯度相关的算法对其更新。因此,即使特征向量非常稀疏,FM 模型也可以正常进行训练

模型还存在一个问题。双线性模型考虑不同特征之间乘积的做法,虽然提升了模型的能力,但也引入了额外的计算开销。可以对上面的公式做一些变形,改变计算顺序来降低时间复杂度

 至此,FM 的预测公式为:

 动手实现因子分解机

class FM:
    def __init__(self, feature_num, vector_dim):
        # vector_dim代表公式中的k,为向量v的维度
        self.theta0 = 0.0 # 常数项
        self.theta = np.zeros(feature_num) # 线性参数
        self.v = np.random.normal(size=(feature_num, vector_dim)) # 双线性参数
        self.eps = 1e-6 # 精度参数

    def _logistic(self, x):
        # 工具函数,用于将预测转化为概率
        return 1 / (1 + np.exp(-x))

    def pred(self, x):
        # 线性部分
        linear_term = self.theta0 + x @ self.theta
        # 双线性部分
        square_of_sum = np.square(x @ self.v)
        sum_of_square = np.square(x) @ np.square(self.v)
        # 最终预测
        y_pred = self._logistic(linear_term \
            + 0.5 * np.sum(square_of_sum - sum_of_square, axis=1))
        # 为了防止后续梯度过大,对预测值进行裁剪,将其限制在某一范围内
        y_pred = np.clip(y_pred, self.eps, 1 - self.eps)
        return y_pred

    def update(self, grad0, grad_theta, grad_v, lr):
        self.theta0 -= lr * grad0
        self.theta -= lr * grad_theta
        self.v -= lr * grad_v

np.clip将预测值限制在 [self.eps, 1 - self.eps] 的范围内,如果预测值超出了这个范围,就将其设置为边界值(因为用了sigmoid)

习题

1.B。只要有不为0的特征就能训练

2.C。C不涉及θ1和θ2之间的乘积或内积,因此不是一个双线性模型;D不是标准的双线性形式,但它可以被视为双线性的,它涉及到了θ1和θ2的乘积

3.题目中这种编码方式叫作label encoder

避免顺序假设: Label Encoder 将类别按照它们出现的顺序进行编码,这可能会给模型引入错误的假设,认为类别之间存在顺序关系

避免偏好性: Label Encoder 可能会给编码后的类别赋予不同的数值,这可能导致模型在训练过程中对数值较大的类别产生偏好

适用性广泛: One-Hot Encoder 适用于大多数机器学习模型,包括线性模型、树模型等

4.

class MF:
    def __init__(self, N, M, d):
        # N是用户数量,M是电影数量,d是特征维度
        # 定义模型参数
        self.user_params = np.ones((N, d))
        self.item_params = np.ones((M, d))
        self.global_bias = 0.0  # 全局打分偏置
        self.user_bias = np.zeros(N)  # 用户打分偏置
        self.item_bias = np.zeros(M)  # 物品打分偏置

    def pred(self, user_id, item_id):
        # 预测用户user_id对电影item_id的打分
        # 获得用户偏好和电影特征
        user_param = self.user_params[user_id]
        item_param = self.item_params[item_id]
        # 计算预测分数
        pred_score = np.sum(user_param * item_param, axis=1)
        pred_score += self.global_bias  # 添加全局打分偏置
        pred_score += self.user_bias[user_id]  # 添加用户打分偏置
        pred_score += self.item_bias[item_id]  # 添加物品打分偏置
        return pred_score

    def update(self, user_grad, item_grad, global_bias_grad, user_bias_grad, item_bias_grad, lr):
        # 根据参数的梯度更新参数
        self.user_params -= lr * user_grad
        self.item_params -= lr * item_grad
        self.global_bias -= lr * global_bias_grad
        self.user_bias -= lr * user_bias_grad
        self.item_bias -= lr * item_bias_grad

def train(model, learning_rate, lbd, max_training_step, batch_size):
    train_losses = []
    test_losses = []
    batch_num = int(np.ceil(len(user_train) / batch_size))
    with tqdm(range(max_training_step * batch_num)) as pbar:
        for epoch in range(max_training_step):
            # 随机梯度下降
            train_rmse = 0
            for i in range(batch_num):
                # 获取当前批量
                st = i * batch_size
                ed = min(len(user_train), st + batch_size)
                user_batch = user_train[st: ed]
                item_batch = item_train[st: ed]
                y_batch = y_train[st: ed]
                # 计算模型预测
                y_pred = model.pred(user_batch, item_batch)
                # 计算梯度
                P = model.user_params
                Q = model.item_params
                errs = y_batch - y_pred
                P_grad = np.zeros_like(P)
                Q_grad = np.zeros_like(Q)
                # 计算全局打分偏置、用户打分偏置和物品打分偏置的梯度
                global_bias_grad = -np.mean(errs)  # 全局打分偏置梯度
                user_bias_grad = np.zeros_like(model.user_bias)
                item_bias_grad = np.zeros_like(model.item_bias)
                for user, item, err in zip(user_batch, item_batch, errs):
                    user_bias_grad[user] += -err
                    item_bias_grad[item] += -err
                    P_grad[user] = P_grad[user] - err * Q[item] + lbd * P[user]
                    Q_grad[item] = Q_grad[item] - err * P[user] + lbd * Q[item]
                model.update(P_grad / len(user_batch), Q_grad / len(user_batch), 
                             global_bias_grad, user_bias_grad / len(user_batch), 
                             item_bias_grad / len(user_batch), learning_rate)
                
                train_rmse += np.mean(errs ** 2)
                # 更新进度条
                pbar.set_postfix({
                    'Epoch': epoch,
                    'Train RMSE': f'{np.sqrt(train_rmse / (i + 1)):.4f}',
                    'Test RMSE': f'{test_losses[-1]:.4f}' if test_losses else None
                })
                pbar.update(1)

            # 计算 RMSE 损失
            train_rmse = np.sqrt(train_rmse / len(user_train))
            train_losses.append(train_rmse)
            y_test_pred = model.pred(user_test, item_test)
            test_rmse = np.sqrt(np.mean((y_test - y_test_pred) ** 2))
            test_losses.append(test_rmse)
    
    return train_losses, test_losses

之前

加了之后可以看到loss低了

5.略

6.对不起,做不到>_<

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1559782.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

0.5米多光谱卫星影像在农业中进行地物非粮化、非农化监测

一、引言 随着科技的发展&#xff0c;卫星遥感技术已经成为了农业领域中重要的数据来源。其中&#xff0c;多光谱卫星影像以其独特的优势&#xff0c;在农业应用中发挥着越来越重要的作用。本文将重点探讨0.5米加2米多光谱卫星影像在农业中的应用。 二、多光谱卫星影像概述 多…

ESP8266 WiFi物联网智能插座—上位机软件实现

1、软件架构 上位机主要作为下位机数据上传服务端以及节点调试的控制端&#xff0c;可以等效认为是专属版本调试工具。针对智能插座协议&#xff0c;对于下位机进行可视化监测和管理。 软件技术架构如下&#xff0c;主要为针对 Windows 的PC 端应用程序&#xff0c;采用WPF以及…

Mock.js的基本使用

mock顾名思义&#xff0c;就是模拟的意思&#xff0c;它模拟什么呢&#xff1f;假设我们在开发的过程中&#xff0c;我们需要使用到接口&#xff0c;但是后端接口并没有完善&#xff0c;那么我们就可以使用到mock.js&#xff0c;它可以随机生成数据&#xff0c;拦截AJAX请求&am…

壁纸小程序Vue3(分类页面和用户页面基础布局)

1.配置tabBar pages.json "tabBar": {"color": "#9799a5","selectedColor": "#28B389","list": [{"text": "推荐","pagePath": "pages/index/index","iconPath&quo…

网络安全-内网渗透2

一、MIC 将我们上次未描述完的MIC在这里详细解释一下 咱们所抓的第二个包会给返回一个服务端的challenge 之后服务器回包的第三个包会回复一个client challenge 所以咱们客户端和服务端现在分别有两个challenge&#xff0c;相当于客户端和服务端互相交换了一下challenge 因此…

《深度学习入门之PyTorch》书籍阅读笔记

《深度学习入门之PyTorch》书籍阅读笔记 ISBN 978-7-121-32620-2 多层全连接神经网络 Pytorch基础 Tensor张量 是一个多维矩阵&#xff0c;pytorch的tensor可以和numpy的ndarray相互转换&#xff0c;但numpy的ndarray只能在CPU上运行。不同数据类型的Tensor&#xff0c;t…

nginx的https与动态负载均衡

nginx的https 证书可以根据你的域名和服务器服务商去进行签发 , 比如 : 阿里云 腾讯云 百度云 华为云等 这里使用的是腾讯云 : 下载证书 : 选择 nginx: 下载之后传递到服务器上。 下面开始配置nginx的https: 1. 解压下载的证书包 cd /etc/ssl unzip xxcc.dwa_nginx.zip mv…

JMeter基础用法和测试WebSocket请求

目录 JMeter websocket插件安装测试接口的编写添加测试线程组创建取样器创建WebSocket连接创建循环控制器创建WebSocket request-response Sampler创建固定定时器 正则匹配上一个请求的数据做为当前请求参数正则编写使用匹配值 CSV文件读取参数添加汇总报告和结果树 JMeter web…

zookeeper如何管理客户端与服务端之间的链接?(zookeeper sessions)

zookeeper客户端与服务端之间的链接用zookeeper session表示。 zookeeper session有三个状态&#xff1a; CONNECTING, ASSOCIATING, CONNECTED, CONNECTEDREADONLY, CLOSED, AUTH_FAILED, NOT_CONNECTED&#xff08;start时的状态&#xff09; 1、CONNECTING 。 表明客户…

vulhub中Apache Solr 远程命令执行漏洞复现(CVE-2017-12629)

Apache Solr 是一个开源的搜索服务器。Solr 使用 Java 语言开发&#xff0c;主要基于 HTTP 和 Apache Lucene 实现。原理大致是文档通过Http利用XML加到一个搜索集合中。查询该集合也是通过 http收到一个XML/JSON响应来实现。此次7.1.0之前版本总共爆出两个漏洞&#xff1a;[XM…

金三银四面试题(九):JVM常见面试题(3)

今天我们继续探讨常见的JVM面试题。这些问题不比之前的问题庞大&#xff0c;多用于面试中JVM部分的热身运动&#xff0c;开胃菜&#xff0c;但是大家已经要认真准备。 你能保证GC 执行吗&#xff1f; 不能&#xff0c;虽然你可以调用System.gc() 或者Runtime.gc()&#xff0c…

vue3+threejs新手从零开发卡牌游戏(二十四):添加p2战斗逻辑

用代码模拟p2战斗逻辑&#xff0c;按流程进行步骤拆分&#xff1a; 1.p2抽卡 2.p2召唤怪兽上场 3.p2战斗 其中战斗部分分为几种情况&#xff1a; 情况一&#xff1a;p2场上卡牌由大到小进行排序&#xff0c;按序轮询可以攻击的卡牌&#xff0c;然后攻击p1场上卡牌由大到小…

代码随想录阅读笔记-二叉树【完全二叉树节点个数】

题目 给出一个完全二叉树&#xff0c;求出该树的节点个数。 示例 1&#xff1a; 输入&#xff1a;root [1,2,3,4,5,6]输出&#xff1a;6 示例 2&#xff1a; 输入&#xff1a;root []输出&#xff1a;0 示例 3&#xff1a; 输入&#xff1a;root [1]输出&#xff1a;1 提示&…

玩转Django分页器

一、Pagination 分页器编程步骤 View, 导入django.core.paginator.Paginator类&#xff0c;创建Paginator 对象时&#xff0c;输入qs对象&#xff0c;以及每页显示条数。 接收 URL, 从请求参数中读取page数值 &#xff0c;通过 paginator.page(page_num) 返回请求页的page_obj…

pyinstaller打包多线程pyqt5程序后,报错,反复弹窗等问题

报错1&#xff1a; Traceback (most recent call last): File “MPL.py”, line 502, in File “Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_multiprocessing.py”, line 45, in _freeze_support ValueError: not enough values to unpack (expected 2, got 1) 报…

绘制空心环形

1.通过几个div拼接绘制空心环形进度条。 通过 -webkit-mask: radial-gradient(transparent 150px, #fff 150px);绘制空心圆 html:<body><div class"circle"><div class"circle-left"></div><div class"circle-left-mask&…

从0开始搭建基于VUE的前端项目(二) 安装和配置element-ui组件库

版本和地址 ElementUI 2.15.14 (https://element.eleme.io/)按需引入的插件 babel-plugin-component(1.1.1) https://github.com/ElementUI/babel-plugin-component 安装 npm install element-ui完整引入(不建议) 这种方式最后打包的源文件很大&#xff0c;造成网络资源的浪…

LeetCode-56. 合并区间【数组 排序】

LeetCode-56. 合并区间【数组 排序】 题目描述&#xff1a;解题思路一&#xff1a;排序&#xff1f;怎么排&#xff1f;当然是排各个区间的左边界&#xff0c;然后判断下一个边界的左边界与结果数组里面的右边界是否重叠。解题思路二&#xff1a;优化解题思路三&#xff1a;0 题…

Vitepress部署到GitHub Pages,工作流

效果&#xff1a; 第一步&#xff1a; 部署 VitePress 站点 | VitePress 执行 npm run docs:build&#xff0c;npm run docs:preview&#xff0c;生成dist文件 第二步&#xff1a; 手动创建.gitignore文件&#xff1a; node_modules .DS_Store dist-ssr cache .cache .temp *…

[html]基础知识点汇总

前言 经过一阵子学习后&#xff0c;把知识点全部提炼了出来&#xff0c;自我感觉比较全和简洁&#xff0c;希望能够帮到大家。 本机实验环境 火狐浏览器&#xff0c;vscode&#xff0c;windows11&#xff0c;程序运行插件&#xff1a;live server html介绍 html--前端语言…