我们是如何测试人工智能的(六)推荐系统拆解

推荐系统简介

推荐系统的问题

根据之前学习到的内容,我们已经基本了解到了要如何构建一个二分类模型。我们都知道模型大体可以分成,回归,二分类和多分类。但推荐系统是属于哪一种场景呢,比如我们常见的广告推荐或者内容推荐,这些场景都是由系统来判断用户的喜好来推送广告或者视频内容,以追求更高的点击率和转化率。这种场景怎么看都不像跟这三种类型的算法有关系。

实现思路

其实解决这个问题的思路也比较简单, 我们可以遵循如下的原则:

  1. 借助专家系统,根据用户的信息初筛一个候选的视频集合(比如 1000 个),比如可以先简单根据用户的年龄,性别,爱好,职业进行推测他喜欢的类型并过滤出候选集合。 这是一种预处理机制, 在人工智能系统中,模型往往无法处理所有的情况,需要一些预处理与后处理辅助模型。在推荐系统中这个步骤往往被称为大排序,先根据规则来筛选候选集合。这么做有多种原因,其中一种比较典型的是担心模型的性能无法支撑过多的候选集合的计算。
  2. 训练一个二分类模型,这个模型用于推理出用户是否会点击这个视频(根据业务场景来,有可能是点击,有可能是点赞,也有可能是转化)。
  3. 将候选集合分别输入给模型进行推理。计算出每个视频会被用户点击的概率。
  4. 把模型的推理结果进行排序,取 top n 个概率最高的视频推送给用户。这一步就与传统的二分类模型不同, 我们已经知道模型输出的是目标属于某个类别的概率。而在传统二分类模型中, 需要用户自己设定一个阈值(也叫置信度)来辅助判断目标的类别, 概率大于这个阈值的判定为正例,小于这个阈值的判定为负例,这正是二分类模型的原理。但是在推荐系统中, 我们并不会因为用户喜欢这个内容的概率超过了某个阈值就进行推送, 因为候选集合太多了, 我们不能把超过某个阈值的都推送过去(广告位或者内容推送是有数量限制的)。 所以最终选择的是根据用户喜欢这个内容的概率进行排序,然后取 topN 来进行推送。

如此我们就把一个推荐系统的问题转换成了一个二分类的问题。 我们可以理解为世界上所有的监督学习场景,都是由二分类,多分类和回归问题变种而来。

写一个简单的模型训练 DEMO(使用 spark ml 库)


from pyspark.sql import SparkSession
from pyspark.ml import Pipeline
from pyspark.ml.feature import Tokenizer, StopWordsRemover, CountVectorizer
from pyspark.ml.classification import LogisticRegression
from pyspark.ml.evaluation import BinaryClassificationEvaluator


from pyspark import SparkContext, SparkConf, SQLContext
from pyspark.sql import functions as F
from pyspark.sql.window import Window

conf = SparkConf().setMaster("local").setAppName("My App")
sc = SparkContext(conf=conf)
sqlContext = SQLContext(sc)


# 定义数据
dicts = [
    ['man', 'The Shawshank Redemption', 1.0],
    ['man', 'The Godfather', 1.0],
    ['man', 'Forrest Gump', 1.0],
    ['woman', 'Titanic', 1.0],
    ['woman', 'Forrest Gump', 0.0],
    ['woman', 'The Godfather', 0.0],
    ['woman', 'The Shawshank Redemption', 0.0],
    ['man', 'Titanic', 0.0],
    ['man', 'A Beautiful Mind', 0.0],
    ['woman', 'A Beautiful Mind', 1.0],
]
rdd = sc.parallelize(dicts, 3)
dataf = sqlContext.createDataFrame(rdd, ['gender', 'title', 'interested'])


# 将性别进行独热编码,以便把数据转换成算法可以识别的形式
from pyspark.ml.feature import StringIndexer, OneHotEncoder, VectorAssembler
stringIndexer = StringIndexer(inputCol="gender", outputCol="gender_num")
data_indexed = stringIndexer.fit(dataf).transform(dataf)
encoder = OneHotEncoder(inputCol="gender_num", outputCol="gender_onehot")
data_encoded = encoder.fit(data_indexed).transform(data_indexed)
data = data_encoded.select('gender_onehot', 'interested', 'title')
data.show()

# 使用分词器
tokenizer = Tokenizer(inputCol="title", outputCol="words")
# 使用停用词
remover = StopWordsRemover(inputCol="words", outputCol="filtered_words")
# 将文本数据转换成特征向量,注意下面被注释的代码,这里是词向量转换,在NLP中,我们经常会把文本进行词向量转换,我们在下面会详细讲解词向量的内容。
# word2vec.fit(remover)
vectorizer = CountVectorizer(inputCol="filtered_words", outputCol="final_words")

# 将所有特征组合成一个特征向量
vectorAssembler = VectorAssembler(inputCols=["gender_onehot", "final_words"], outputCol="features")

# 定义逻辑回归
classifier = LogisticRegression(labelCol="interested", featuresCol="features", maxIter=10)
# 定义流水线, 当数据来了以后就可以按顺序处理数据
pipeline = Pipeline(stages=[tokenizer, remover, vectorizer, vectorAssembler, classifier])
# 模型训练
model = pipeline.fit(data)

# 模型推理
predictions = model.transform(data)
evaluator = BinaryClassificationEvaluator(labelCol="interested", rawPredictionCol="rawPrediction")
accuracy = evaluator.evaluate(predictions)
print("Accuracy:", accuracy)
predictions.show()

df_desc = predictions.orderBy(F.desc("probability"))
df_desc.show()

词向量

上面用于训练模型的数据中有一列是视频的标题,我们会发现代码中我们使用了一系列 NLP(Natural Language Processing,自然语言处理)的算法:

  • 分词器(tokenizer):用于在一个句子中提取一个一个的词
  • 停用词(stop words):用于去掉一些语义无关的语气词,介词等,比如the或者中文中的语气词。 在模型训练中往往需要去掉这些词以去除噪音,优化模型空间,减少索引量等等
  • 词向量(也叫词嵌入):可以理解为计算出词与词之间的关联性,从而训练出的围绕中心词的特征向量。

上述概念中可能词向量是最难以理解的,这里尽量尝试用简单易懂的语言来解释这个概念。 我们之前训练反欺诈模型的时候,也遇到过一些离散特征,比如title也是以文本形式存在的数据。
我们在反欺诈中处理这样的使用的 one-hot(独热编码),独热编码也是一种处理离散特征常用的方法。假设我们有一群学生,他们可以通过四个特征来形容,分别是:

  • 性别:[“男”,“女”]
  • 年级:[“初一”,“初二”,“初三”]
  • 学校:[“一中”,“二中”,“三中”,“四中”] 我们用采用 N 位状态寄存器来对 N 个状态进行编码,拿上面的例子来说,就是:

因此,当我们再来描述一个学生的时候(男生,初一,来自一中),就可以采用 [1 0 1 0 0 0 1 0 0] 这样的形式来表示。这也一种用于特征组合的实现方法之一。

或者我们也可以使用类似 bitmap 的方法做出一个 one—hot 向量来表示离散特征。 我们可以用类似下面的形式表达:

假设职业这一列一共有 100 个值, 假设教师在编号 6 这个位置上,编号 6 所在位置 ide 值就是 1,其他的值都是 0,我们以这个向量来代表教师这个特征. 以此类推,如果学生代表的编号是 10,那么
10 这个位置所在的值是 1,其他位置的值都是 0,用词向量来代表学生。 这样最后我们就有 100 个 100 维度的向量来表示这些特征。

上面两种方法都是很常见的用来用来表达文本特征的方法,但它们的问题是词与词之间是独立的,互相没有关联。 比如我们的训练数据中有一个句子this is apple juice,我们期望当
出现 this is orange __ 的时候,模型能够为我们推测出这个空白处也应该填写单词juice。 也就是我们希望模型能通过之前针对第一个句子的训练就能找到单词与单词之间的关系,模型能够知道
appleorange是含义相似的词,从而能推测出orange后面也可以填写juice。 而这正是词向量要做的事情。

如上图,词向量围绕这一些中心词(性别,事务,高贵程度),计算出每一个词与这些中心词的相关程度。而要得到这个词向量本身就需要相关算法训练出来,比如 world2vec:


from pyspark.ml.feature import Word2Vec

from pyspark.sql import SparkSession

spark = SparkSession \
    .builder \
    .appName("dataFrame") \
    .getOrCreate()

# Input data: Each row is a bag of words from a sentence or document.
documentDF = spark.createDataFrame([
    ("Hi I heard about Spark".split(" "),),
    ("I wish Java could use case classes".split(" "),),
    ("Logistic regression models are neat".split(" "),)
], ["text"])

documentDF.show()

# Learn a mapping from words to Vectors.
word2Vec = Word2Vec(vectorSize=5, minCount=0, inputCol="text", outputCol="result")
model = word2Vec.fit(documentDF)

result = model.transform(documentDF)
for row in result.collect():
    text, vector = row
    print("Text: [%s] => \nVector: %s\n" % (", ".join(text), str(vector)))

# 运行结果
Text: [Hi, I, heard, about, Spark] => 
Vector: [-0.056896060705184937,-0.029017432034015658,0.020092955231666567,-0.0003222271800041199,-0.02170231742784381]

Text: [I, wish, Java, could, use, case, classes] => 
Vector: [0.003511839745832341,0.025775608922620968,-0.01780955892588411,0.0012207544807876858,-0.0023355478021715365]

Text: [Logistic, regression, models, are, neat] => 
Vector: [0.008233835361897946,-0.01995183974504471,-0.013863331265747549,-0.051928133144974714,-0.014838875085115433]

上面是一个训练词向量的代码,它的计算原理大概可以描述为:在文本中选取中心词并选取中心词前后数个单词,并训练出这些词会出现在中心词周围的概率。

总结

这些就是一个推荐系统中的大概步骤, 当然实际的推荐系统是非常复杂的, 我目前也只是列了一个简单的 DEMO,帮助大家理解推荐系统都在做什么事情。 更多手把手教你人工智能测试内容请关注我的星球:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

线性代数---------学习总结

线性代数之行列式 行列式的几条重要的性质 1.某两行某两列交换位置之后,值变号 2.行列式转置,值不变 3.范德蒙德行列式,用不同行的公比做一系列的累乘运算 4.把某一行的行列式加到另一行上,利用他们之间的倍数关系&#xff0…

最小二乘圆柱拟合(高斯牛顿法)

欢迎关注更多精彩 关注我,学习常用算法与数据结构,一题多解,降维打击。 本期话题:最小二乘圆柱拟合 相关背景资料 点击前往 圆柱拟合输入和输出要求 输入 8到50个点,全部采样自圆柱上。每个点3个坐标,坐…

云计算底层技术奥秘、磁盘技术揭秘、虚拟化管理、公有云概述

云计算基础 实验图例 虚拟化平台安装 创建虚拟机 1、创建虚拟机 2cpu,4G内存(默认用户名: root 密码: a) 2、验证 ecs 是否支持虚拟化 [rootecs ~]# grep -Po "vmx|svm" /proc/cpuinfovmx... ...[rootecs ~]# lsmod |grep kvm…

Android 13以上版本读写SD卡权限适配

如题&#xff0c;最近工作上处理的问题&#xff0c;把解决方案简单逻列出来&#xff0c;供有需要的朋友参考之 解决方案&#xff1a; 1、配置权限 <uses-permission android:name"android.permission.READ_MEDIA_IMAGES" /><uses-permission android:name&q…

Security ❀ HTTP/HTTPS逐包解析交互过程细节

文章目录 1. TCP三次握手机制2. HTTP Request 请求报文3. HTTP Response 响应报文4. SSL/TLS协议4.1. ClientHello 客户端Hello报文4.2 ServerHello 服务器Hello报文4.3. *ServerKeyExchange 服务公钥交换4.4. ClientKeyExchange 客户端公钥交换4.5. *CertificateVerify 证书验…

Mysql 更新数据

MySQL中使用UPDATE语句更新表中的记录&#xff0c;可以更新特定的行或者同时更新所有的行。基本语法结构如下&#xff1a; UPDATE table_name SET column_name1 value1,column_name2 value2,……, column_namen valuen WHERE(condition); column_name1,column_name2,……,…

Oracle数据库如何对中文的一二三四五六七八九十数进行正序排列排序

引言 在处理包含中文数字的数据库查询时&#xff0c;我们可能会遇到一个看似简单实则充满挑战的问题——如何让Oracle数据库按照阿拉伯数字顺序&#xff08;1, 2, 3, 4, 5, 6…&#xff09;对诸如“一、二、三、四、五、六…”这样的中文数字进行排序。这不仅是一个技术性问题&…

长度最小的子数组[中等]

一、题目 给定一个含有n个正整数的数组和一个正整数target。找出该数组中满足其总和大于等于target的长度最小的连续子数组[numsl, numsl1, ..., numsr-1, numsr]&#xff0c;并返回其长度。如果不存在符合条件的子数组&#xff0c;返回0。 示例 1&#xff1a; 输入&#xff…

数据结构奇妙旅程之七大排序

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN …

如何做好阅读笔记搭建个人知识体系?

一、阅读笔记的重要性 阅读笔记在个人知识管理中扮演着重要的角色。通过记录、整理和利用阅读笔记&#xff0c;可以帮助我们更好地梳理个人知识体系&#xff0c;提高阅读效率&#xff0c;并且能够为个人成长提供有力支持。阅读笔记的重要性不言而喻&#xff0c;它是我们通过阅…

芯片替代查询指南:如何在电子设计中选择最佳替代方案

在电子制造与维修的世界里&#xff0c;芯片的选择和替代是一个常见且复杂的过程。选择正确的芯片替代对于确保电路的正常工作以及产品的性能不可或缺。本篇文章将为您提供关于芯片替代查询网站的全面指南。 什么是芯片替代查询&#xff1f; 芯片替代查询是提供芯片替代选项查…

时序数据库 Tdengine 执行命令能够查看执行的sql语句

curl是 访问6041端口&#xff0c;在windows系统里没有linux里的curl命令&#xff0c;需要用别的工具实现。我在cmd里是访问6030端口 第一步 在安装是时序数据库的服务器上也就是数据库服务端 进入命令窗口 执行 taos 第二步 执行 show queries\G;

OpenAI发布新模型!ChatGPT性能重磅提升,API大幅降价,GPT-4 「变懒」被修复

OpenAI 对ChatGPT进行了大更新&#xff1a;推出了新一代的嵌入模型&#xff0c;对GPT-4 Turbo模型进行了更新&#xff0c;并将很快对GPT-3.5 Turbo的API进行大幅降价&#xff0c;GPT-4「变懒」行为也被修复。 接下来二狗就带大家看看ChatGPT的这次详细更新。 推出新的嵌入模型…

PyQt5零基础入门(七)——文本编辑框

单行文本框控件(QLineEdit) QLineEdit是一个小部件&#xff0c;通常用于创建用户界面中的文本输入框。它提供了简单而强大的文本编辑功能&#xff0c;适用于各种需要单行文本输入的应用程序。 from PyQt5.QtWidgets import * import sysclass Window(QWidget):def __init__(s…

RK3568平台开发系列讲解(Linux系统篇)device 资源的获取

🚀返回专栏总目录 文章目录 一、platform_device 结构体二、platform_get_resource() 获取沉淀、分享、成长,让自己和他人都能有所收获!😄 一、platform_device 结构体 struct platform_driver 结构体继承了 struct device_driver 结构体, 因此可以直接访问 struct devi…

Python qt.qpa.xcb: could not connect to display解决办法

遇到问题&#xff1a;qt.qpa.xcb: could not connect to display 解决办法&#xff0c;在命令行输入&#xff1a; export DISPLAY:0 然后重新跑python程序&#xff0c;解决&#xff01; 参考博客&#xff1a;qt.qpa.xcb: could not connect to displayqt.qpa.plugin: Could …

Spring Boot + security + jwt 测试安全策略

一、测试概述 主要目的是测试security的用法。因测试搭建mysql和redis比较麻烦&#xff0c;所以我这里将自定义的jwt和用户信息缓存到程序的内存中。 本人测试的项目比较混乱&#xff0c;Spring Boot父类只标出有用的依赖。其子类用的版本为jdk11。后续会继续深入oauth2&#x…

【web安全】文件上传漏洞

upload-labs靶场 第一关 绕过前端 先打开哥斯拉&#xff0c;生成木马&#xff0c;选择php 打开brup开浏览器&#xff0c;上传文件&#xff0c;就会发现被阻止了&#xff0c;还没抓到包呢 那就是被前端代码阻止了&#xff0c;那通常前端代码都只能防御后缀名 我们抓到包后直…

分布式因果推断在美团履约平台的探索与实践

美团履约平台技术部在因果推断领域持续的探索和实践中&#xff0c;自研了一系列分布式的工具。本文重点介绍了分布式因果树算法的实现&#xff0c;并系统地阐述如何设计实现一种分布式因果树算法&#xff0c;以及因果效应评估方面qini_curve/qini_score的不足与应对技巧。希望能…

Android MediaCodec 简明教程(四):使用 MediaCodec 将视频解码到 Surface,并使用 SurfaceView 播放视频

系列文章目录 Android MediaCodec 简明教程&#xff08;一&#xff09;&#xff1a;使用 MediaCodecList 查询 Codec 信息&#xff0c;并创建 MediaCodec 编解码器Android MediaCodec 简明教程&#xff08;二&#xff09;&#xff1a;使用 MediaCodecInfo.CodecCapabilities 查…